引入线程池的背景
为什么需要线程池呢?
设想一下,如果我们使用有任务就开启一个子线程处理,处理完成后,销毁子线程或等得子线程自然死亡,那么如果我们的任务所需时间比较短,但是任务数量比较多,那么更多的时间是花在线程的创建和结束上面,效率肯定就低了。
线程池的原理:
既然是线程池(Thread pool),其实名字很形象,就是把指定数量的可用子线程放进一个"池里",有任务时取出一个线程执行,任务执行完后,并不立即销毁线程,而是放进线程池中,等待接收下一个任务。这样内存和cpu的开销也比较小,并且我们可以控制线程的数量。
线程池的实现:
线程池有很多种实现方式,在python中,已经给我们提供了一个很好的实现方式:Queue-队列。因为python中Queue本身就是同步的,所以也就是线程安全的,所以我们可以放心的让多个线程共享一个Queue。
那么说到线程池,那么理应也得有一个任务池,任务池中存放着待执行的任务,各个线程到任务池中取任务执行,那么用Queue来实现任务池是最好不过的。
5.实例
1 public class MyThread extends Thread { 2 @Override 3 publicvoid run() { 4 System.out.println(Thread.currentThread().getName() + "执行中。。。"); 5 } 6 }
①newSingleThreadExecutor
1 publicclassTestSingleThreadExecutor { 2 publicstaticvoid main(String[] args) { 3 //创建一个单个线程的线程池 4 ExecutorService pool = Executors. newSingleThreadExecutor(); 5 //创建实现了Runnable接口对象 6 Thread tt1 = new MyThread(); 7 Thread tt2 = new MyThread(); 8 Thread tt3 = new MyThread(); 9 Thread tt4 = new MyThread(); 10 Thread tt5 = new MyThread(); 11 //将线程放入池中并执行 12 pool.execute(tt1); 13 pool.execute(tt2); 14 pool.execute(tt3); 15 pool.execute(tt4); 16 pool.execute(tt5); 17 //关闭 18 pool.shutdown(); 19 } 20 } 21 result: 22 pool-1-thread-1执行中。。。 23 pool-1-thread-1执行中。。。 24 pool-1-thread-1执行中。。。 25 pool-1-thread-1执行中。。。 26 pool-1-thread-1执行中。。。
②newFixedThreadExecutor
1 public class TestFixedThreadPool { 2 publicstatic void main(String[] args) { 3 //创建一个可重用固定线程数的线程池 4 ExecutorService pool = Executors.newFixedThreadPool(2); 5 //创建实现了Runnable接口对象 6 Thread t1 = new MyThread(); 7 Thread t2 = new MyThread(); 8 Thread t3 = new MyThread(); 9 Thread t4 = new MyThread(); 10 Thread t5 = new MyThread(); 11 //将线程放入池中进行执行 12 pool.execute(t1); 13 pool.execute(t2); 14 pool.execute(t3); 15 pool.execute(t4); 16 pool.execute(t5); 17 //关闭线程池 18 pool.shutdown(); 19 } 20 } 21 result: 22 pool-1-thread-1执行中。。。 23 pool-1-thread-2执行中。。。 24 pool-1-thread-1执行中。。。 25 pool-1-thread-2执行中。。。 26 pool-1-thread-1执行中。。。
③newCacheThreadExecutor
1 public class TestCachedThreadPool { 2 publicstaticvoid main(String[] args) { 3 //创建一个可缓存线程数的线程池 4 ExecutorService pool = Executors.newCachedThreadPool(); 5 //创建实现了Runnable接口对象 6 Thread t1 = new MyThread(); 7 Thread t2 = new MyThread(); 8 Thread t3 = new MyThread(); 9 Thread t4 = new MyThread(); 10 Thread t5 = new MyThread(); 11 //将线程放入池中进行执行 12 pool.execute(t1); 13 pool.execute(t2); 14 pool.execute(t3); 15 pool.execute(t4); 16 pool.execute(t5); 17 //关闭线程池 18 pool.shutdown(); 19 } 20 } 21 result: 22 pool-1-thread-1执行中。。。 23 24 pool-1-thread-2执行中。。。 25 pool-1-thread-4执行中。。。 26 pool-1-thread-3执行中。。。 27 pool-1-thread-5执行中。。。
线程池的注意事项:
虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。在使用线程池时需注意线程池大小与性能的关系,注意并发风险、死锁、资源不足和线程泄漏等问题。
1、线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。
一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。
如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
2、并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
3、线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。
线程池要点:
线程池要点: 1、通过判断等待的任务数量和线程池中的最大值,取最小值来判断开启多少线程来工作 比如: 任务数是3,进程池最大20 ,那么咱们只需要开启3个线程就行了。 任务数是500,进程池是20,那么咱们只开20个线程就可以了。 取最小值 2、实现线程池正在运行,有一个查看的功能,查看一下现在线程里面活跃的线程是多少等待的是多少? 线程总共是多少,等待中多少,正在运行中多少 作用: 方便查看当前线程池状态 能获取到这个之后就可以当线程一直处于空闲状态 查看状态用:上下文管理来做,非常nice的一点 3、关闭线程