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时,会动态创建与回收线程池里面的线程的资源。

 

更多线程安全链接:

posted @ 2017-12-27 17:31  晕菜一员  阅读(328)  评论(0编辑  收藏  举报