Season 5 ep 6,关于线程池你应该知道的事
Video Link
One Thread, One Thing
且说孔明自引一军屯于五丈原,累令人搦战,魏兵只不出。孔明乃取巾帼并妇人缟素之服,盛于大盒之内,修书一封,遣人送至魏寨。诸将不敢隐蔽,引来使入见司马懿。懿对众启盒视之,内有巾帼妇人之衣,并书一封。懿拆视其书,略曰:“仲达既为大将,统领中原之众,不思披坚执锐,以决雌雄,乃甘窟守土巢,谨避刀箭,与妇人又何异哉!今遣人送巾帼素衣至,如不出战,可再拜而受之。倘耻心未泯,犹有男子胸襟,早与批回,依期赴敌。”司马懿看毕,心中大怒,乃佯笑曰:“孔明视我为妇人耶!”即受之,令重待来使。懿问曰:“孔明寝食及事之烦简若何?”使者曰:“丞相夙兴夜寐,罚二十以上皆亲览焉。所啖之食,日不过数升。”懿顾谓诸将曰:“孔明食少事烦,其能久乎?” ——《三国演义》第一百十三回 上方谷司马受困 五丈原诸葛禳星
论智计谋略,诸葛亮要胜过司马懿,然而,由于丞相不论巨细事必躬亲,只落得星落五丈原的悲惨结局。可见,人的精力是有限的,不应当把所有的任务都压在同一个人头上。在计算机科学中,这一理论同样适用。
对于线程,有专门处理音频的Audio Thread,有响应IO的IO Thread,还有负责网络事务的Networking Thread,这些都是良好的编程典范。
然而,存在这样的情况:当某一系列的任务数量实在太多,已经超过了单个线程所能承受的极限。这时我们应当怎么做?如果工作线程有且只能由一个,就像诸葛丞相一样不得不亲力亲为,那可就GG了。不必担心,新时代的计算机科学已经让我们习得了秘-影分身之术。
看一看下面这个场景,页面里需要展示40张图片,每张图片的解码消耗4ms时间,如果只有单一线程进行操作,总共需要用掉160ms。这显然是难以接受的。
如果我们用10条线程并行计算,可以把时间压缩到16ms。
Thread Pool Executor
有了并发处理这一个思路,便可以借助于JAVA提供的强力工具ThreadPoolExecutor来执行任务了。你只需要声明需要的线程数,并且把拆分好的Task传递给ThreadPoolExecutor,ThreadPoolExecutor自身会处理任务分配、线程调度、线程回收等操作。
The More, The Better?
Thread is evil
并发的线程数并非越多越好,首先,一台主机能够开启的线程数是有上限的;其次,线程之间切换本身有代价(线程同步、锁与互斥、环境准备等等);再次,跟CPU数有关,CPU越多,最大性能下可并发线程数越多。
最佳线程数要在实践中通过压测等手段获知。通常我们开发中控制并行线程数=CPU数即可。
每条线程至少占用64k
的内存
ThreadPoolExecutor
的构造过程中,允许我们控制初始线程数、最大线程数。需要注意的是,通过Runtime.getRuntime().availableProcessors()
获取到的内核数也许会小于真实CPU数——出于节约电量等考虑,Android系统会让某些CPU在空闲时处于休眠状态,这些CPU不会被计数。
1 | private static int NUM_OF_CORES = Runtime.getRuntime().availableProcessors(); |
For Power Users Like U
RenderScript是专门用来进行大量并行计算的工具,当你有这方面的需求时,不妨尝试一下。
Use Systrace to Analyze
用以分析线程、内存的Systrace工具在前文已经介绍过,这里不再赘述。