Java 线程池
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其他更多资源。
在JAVA中更加如此,虚拟机将试图跟踪每一个对象,以便能在对象销毁后进行垃圾回收。
所以提供服务程序效率的一个手段,就是尽可能减少创建和销毁对象的次数。
特别是一些很消耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些“池化资源”技术产生的原因。比如大家所熟悉的数据库连接池。
Java线程池实现了一个Java高并发的、Java多线程的、可管理的统一调度器。
package路径:java.util.concurrent.Executors
常用线程池的方法:
newSingleThreadExecutor:创建一个单线程的线程池。
newFixedThreadPool:创建固定大小的线程池。
newCachedTheadPool:创建一个可缓存的线程池。
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor
创建一个单个线程池。这个线程池只有一个线程在工作,相当于单线程串行执行所有任何。此线程池保证所有任何的执行顺序都按照任何的提交顺序执行。
1 public static void main(String[] args) { 2 3 ExecutorService executor = Executors.newSingleThreadExecutor(); 4 5 for (int i = 0; i < 10; i ++) { 6 7 final int no = i; 8 9 Runnable runnable = new Runnable() { 10 @Override 11 public void run() { 12 13 try { 14 15 System.out.printf("into:" + no); 16 17 Thread.sleep(1000); 18 19 System.out.printf("end:" + no); 20 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 }; 26 27 executor.execute(runnable); 28 } 29 30 executor.shutdown(); 31 32 System.out.printf("Thread Main End"); 33 }
运行结果:
1 into:0 2 Thread Main End 3 end:0 4 into:1 5 end:1 6 into:2 7 end:2 8 ... 9 ... 10 into:8 11 end:8 12 into:9 13 end:9
Executors.newSingleThreadExecutor()的实现方法:
1 public static ExecutorService newSingleThreadExecutor() { 2 3 return new FinalizableDelegateExecutorService( 4 new ThreadPoolExecutor(1, 1 5 , 0L, TimeUnit.MILLISECONDS 6 , new LinkedBlockingDeque<Runnable>())); 7 }
上面代码根据ThreadPoolExecutor创建一个LinkedBlockingQueue的一个大小的线程池。采用默认的异常策略。
newCachedTheadPool
创建一个缓存池大小可根据需要伸缩的线程池,但是在以前构造的线程可用时将重用它们。
对于执行短期异步任务的程序而言,这些线程池通常可提高程序性能。调用execute将重用以前构造的线程(如果线程可用),如果现有线程没有可用的,则创建一个新线程并添加到池中。
终止并从缓存中移除那些已有60s未被使用过的线程。因此,长时间保持空闲的线程池不会使用任何资源。
1 public static void main(String[] args) { 2 3 ExecutorService executor = Executors.newCachedThreadPool(); 4 5 for (int i = 0; i < 20; i ++) { 6 7 final int no = i; 8 9 Runnable runnable = new Runnable() { 10 @Override 11 public void run() { 12 13 try { 14 15 System.out.printf("into:" + no); 16 17 Thread.sleep(1000); 18 19 System.out.printf("end:" + no); 20 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 }; 26 27 executor.execute(runnable); 28 } 29 30 executor.shutdown(); 31 32 System.out.printf("Thread Main End"); 33 }
运行结果:
1 into:0 2 into:2 3 into:4 4 ... 5 ... 6 into:15 7 into:18 8 into:19 9 Thread Main End 10 end:2 11 end:1 12 end:3 13 ... 14 ... 15 end:10 16 end:15 17 end:17
从结果上看,一下子所有的线程都开始执行,都在互相的争抢CPU资源。
main线程一让出资源,线程池里面有20个线程同时执行,致时候执行时间也最短,如果CPU运行执行,也是最快的。
Executors.newCachedThreadPool() 的实现方法:
1 public static ExecutorService newCachedThreadPool() { 2 3 return new ThreadPoolExecutor( 4 0, Integer.MAX_VALUE 5 , 60L, TimeUnit.SECONDS 6 , new SynchronousQueue<Runnable>()); 7 }
创建了一个内核线程池,线程为0,来一个线程就在线程池里面创建一个SynchronousQueue。
newFixedThreadPool
创建一个可重用固定线程数的线程池,以共享的无界队列方式来进行这些线程。
如果在所有线程处于活动状态时,提交附加任务,则在又可用线程之前,附加任务将在队列中等待。
如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续任务(如果需要)。
在某个线程被显式地关闭之前,池中的线程将一直存在。
1 public static void main(String[] args) { 2 3 ExecutorService executor = Executors.newFixedThreadPool(5); 4 5 for (int i = 0; i < 20; i ++) { 6 7 final int no = i; 8 9 Runnable runnable = new Runnable() { 10 @Override 11 public void run() { 12 13 try { 14 15 System.out.printf("into:" + no); 16 17 Thread.sleep(1000); 18 19 System.out.printf("end:" + no); 20 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 }; 26 27 executor.execute(runnable); 28 } 29 30 executor.shutdown(); 31 32 System.out.printf("Thread Main End"); 33 }
运行结果:
1 into:0 2 into:2 3 Thread Main End 4 into:4 5 into:1 6 into:3 7 end:0 8 into:5 9 end:2 10 ... 11 ... 12 end:14 13 into18
从结果上看,一下子只有5个线程开始执行,然后结束一个,再执行一个。
Executors.newFixedThreadPool 的实现:
1 public static ExecutorService newFixedThreadPool(int nThreads) { 2 3 return new ThreadPoolExecutor( 4 nThreads, nThreads 5 , 0L, TimeUnit.MILLISECONDS 6 , new LinkedBlockingQuque<Runnable>()); 7 }
创建了一个指定大小的LinkedBlockingQueue的线程池。
线程池:
(1)降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗
(2)提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。
(4)防止服务器过载,形成内存溢出,或者CPU耗尽。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
但如果对多线程使用不当,会增加单个任何的处理时间。
线程池应用范围:
(1)需要大量的线程来完成任务,且完成任务的时间比较短。Web服务器完成网页请求这样的任务,使用线程池技术是非常合适的,因为单个任务小,而任务数量巨大。
但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话比线程的创建时间大多了。
(2)对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
(3)接受突发性的大量请求,但不至于是服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池的情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能是内存到达极限,并出现OutOfMenory的错误。
线程池核心的两个队列:
(1)线程等待池:即线程队列BlockingQueue
(2)任务处理池(PoolWorker):即正在工作的Thread列表(HashSet<Worker>)。
线程池核心的参数:
(1)核心池大小(corePoolSize):即固定大小,设定好之后,线程池的稳定峰值,达到这个值之后池的线程数大小不会释放的。
(2)最大处理线程数(maximumPoolSize):当线程池里面的线程超过corePoolSize,小于maximumPoolSize时,会动态创建与回收线程池里面的线程的资源。
更多线程安全链接: