Java线程池(一):初识
1、什么是线程池?
简单粗暴的理解就是:装着一个或多个线程的容器,我们称这个容器为线程池。
在现实世界中,有着各种各样的“池”,例如游泳池、花池等等。那花池来说,里面种满了各种各样的鲜花,花池本身要做的就是提供一片空地,里面的鲜花是园丁来种植、浇水。不同于花池,线程池不仅仅需要提供一个可以装线程的容器,同时还要承担起管理容器里中线程的职责(例如,新建线程、使用线程等)。
一个简单的线程池应该至少包含下面4个部分:
(1)线程池管理器:创建、销毁并管理线程池,将工作线程放入线程池中
(2)工作线程:一个可以循环执行任务的线程,在没有任务时进行等待
(3)任务队列:将没有处理的任务放在任务队列中,提供一种缓冲机制
(4)任务接口:主要用来规定任务的的入口、任务完成后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行
下面举一个简单的例子来理解线程池的工作:
将线程比作一个机器人,一个线程池就是有很多机器人的工厂,工厂通过新建机器人、分配任务给机器人等方式管理者工厂中的所有机器人。初始时,工厂中并没有机器人。当工厂接到第一个任务时,工厂的管理人员首先新建机器人1号,然后再将任务分配给机器人1号,机器人1号收到任务的命令后就开始工作了。紧接着,工厂接到了第二个任务,管理人员又新建了机器人2号,将第二个任务分配给了机器人2号。当工厂再次接到新的任务时,重复前面的过程。然而,工厂不可能无限制地新建机器人:新建机器人也是需要材料和消耗资源的,资源是有限的;当有新的任务时,前面新建的机器人可能已经完成了交给它的任务而处于空闲状态,因此可以将新的任务分配给处于休息状态的机器人。当工厂中所有的机器人都处于工作状态时,又来了新的任务,怎么办呢?于是,工厂管理者设置了一个任务等候区,新来的任务按顺序在等候区排队。当有机器人完成了任务时,从任务队列中取出一个任务,分配给空闲的机器人,让机器人重新处于工作状态。
2、为什么要使用线程池?
从前面工厂的例子中可以看出,线程池为我们带来了以下好处:
(1)分摊创建线程的开销,加快对请求的响应速度。接到一个任务时,新建了一个线程用于完成任务。当任务完成后,线程处于空闲状态,后面再有新的任务时,不必再新建线程,处于空闲状态的线程可以直接投入工作,省去了新建线程了过程,加快了对请求的响应速度。由于对线程实现了重用,新建线程的开销就被分摊到了多个任务上,线程被重用的次数越多,平均分摊到每个任务中的线程创建的开销就越少。
(2)解决了资源不足的问题。线程是需要资源的,而资源又是有限的,通过控制线程的数目,避免出现资源不足的问题。
3、如何使用线程池?
线程池有着上面的优势,可以自己编码实现一个线程池。然而,编码实现线程池本身就是一项工作量不小的任务,而我们的工作重心是要完成实际的业务。同时,自己编码实现的线程池的正确性、可靠性与稳定性难以保证。于是,大师Doug Lea编写并开源了一个并发程序的代码库,在JDK 1.5之后已加入JDK的豪华套餐,为 java.util.concurrent包。
在Java中,线程池类为java.util.concurrent.ThreadPoolExecutor,可以通过该类创建我们需要的线程池:
1 public ThreadPoolExecutor(int corePoolSize, 2 int maximumPoolSize, 3 long keepAliveTime, 4 TimeUnit unit, 5 BlockingQueue<Runnable> workQueue, 6 RejectedExecutionHandler handler)
参数说明:
corePoolSize : 线程池维护线程的数量(the number of threads to keep in the pool, even if they are idle.)
maximumPoolSize : 线程池维护线程的最大数量(the maximum number of threads to allow in the pool.)
keepAliveTime : 线程池中的线程允许的最大空闲时间(when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.)
unit : 参数keepAliveTime的时间单位 (the time unit for the keepAliveTime argument.)
workQueue : 任务缓冲队列(the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.)
handler : 对拒绝任务的处理策略(the handler to use when execution is blocked because the thread bounds and queue capacities are reached.)
通过上面的构造函数,可以构造一个线程池对象threadPool。在Java中,一个任务就是一个Runnable类的对象,具体的业务处理逻辑在该对象的run()方法中实现。通过threadPoool.execute(Runnable)方法,任务被提交到了线程池中并执行。
【代码实例】
任务类:
1 package com.code.threadpool; 2 3 import java.io.Serializable; 4 5 public class ThreadPoolTask implements Runnable, Serializable { 6 7 private static final long serialVersionUID = 1L; 8 9 private Object threadPoolTaskData; 10 11 ThreadPoolTask(Object tasks) { 12 this.threadPoolTaskData = tasks; 13 } 14 15 public void run() { 16 System.out.println("start ..." + threadPoolTaskData); 17 try { 18 Thread.sleep(2000); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 System.out.println("end ..." + threadPoolTaskData); 23 threadPoolTaskData = null; 24 } 25 26 }
线程池测试类:
1 package com.code.threadpool; 2 3 import java.util.concurrent.ArrayBlockingQueue; 4 import java.util.concurrent.ThreadPoolExecutor; 5 import java.util.concurrent.TimeUnit; 6 7 public class ThreadPoolTest { 8 9 /** 10 * 核心线程数量,线程池维护线程的最少数量 11 */ 12 private static final int corePoolSize = 2; 13 14 /** 15 * 线程池维护线程的最大数量 16 */ 17 private static final int maxPoolSize = 4; 18 19 /** 20 * 线程池维护线程所允许的空闲时间 21 */ 22 private static final int keepAliveTime = 3; 23 24 /** 25 * 线程池所使用的缓冲队列的大小 26 */ 27 private static final int workQueueSize = 3; 28 29 /** 30 * 线程执行任务后sleep的时间,sleep是为了便于观察程序的运行结果 31 */ 32 private static final int produceTaskSleepTime = 2; 33 34 /** 35 * 任务的最大数量 36 */ 37 private static final int produceTaskMaxNumber = 10; 38 39 /** 40 * 任务名称的前缀 41 */ 42 private static final String taskNamePrefix = "task@"; 43 44 public static void main(String[] args) { 45 46 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(corePoolSize, 47 maxPoolSize, keepAliveTime, TimeUnit.SECONDS, 48 new ArrayBlockingQueue<Runnable>(workQueueSize), 49 new ThreadPoolExecutor.DiscardOldestPolicy()); 50 51 for (int i = 0; i < produceTaskMaxNumber; i++) { 52 String taskName = taskNamePrefix + (i + 1); 53 System.out.println(String.format("put %s", taskName)); 54 55 System.out.println("--- before put " + taskName 56 + ", active thread count: " + threadPool.getActiveCount()); 57 ThreadPoolTask task = new ThreadPoolTask(taskName); 58 threadPool.execute(task); 59 System.out.println("--- after put " + taskName 60 + ", active thread count: " + threadPool.getActiveCount()); 61 62 try { 63 Thread.sleep(produceTaskSleepTime); 64 } catch (InterruptedException e) { 65 e.printStackTrace(); 66 } 67 } 68 } 69 70 }
【运行结果】
1 put task@1 2 --- before put task@1, active thread count: 0 3 --- after put task@1, active thread count: 0 4 start ...task@1 5 put task@2 6 --- before put task@2, active thread count: 1 7 --- after put task@2, active thread count: 1 8 start ...task@2 9 put task@3 10 --- before put task@3, active thread count: 2 11 --- after put task@3, active thread count: 2 12 put task@4 13 --- before put task@4, active thread count: 2 14 --- after put task@4, active thread count: 2 15 put task@5 16 --- before put task@5, active thread count: 2 17 --- after put task@5, active thread count: 2 18 put task@6 19 --- before put task@6, active thread count: 2 20 --- after put task@6, active thread count: 2 21 start ...task@6 22 put task@7 23 --- before put task@7, active thread count: 3 24 --- after put task@7, active thread count: 3 25 start ...task@7 26 put task@8 27 --- before put task@8, active thread count: 4 28 --- after put task@8, active thread count: 4 29 put task@9 30 --- before put task@9, active thread count: 4 31 --- after put task@9, active thread count: 4 32 put task@10 33 --- before put task@10, active thread count: 4 34 --- after put task@10, active thread count: 4 35 end ...task@1 36 start ...task@8 37 end ...task@2 38 start ...task@9 39 end ...task@6 40 start ...task@10 41 end ...task@7 42 end ...task@8 43 end ...task@9 44 end ...task@10
参考资料:
1、http://blog.itpub.net/9399028/viewspace-1852029/
2、http://blog.csdn.net/newchenxf/article/details/51996950
3、http://uule.iteye.com/blog/1123185
4、http://www.cnblogs.com/dolphin0520/p/3932921.html
5、https://segmentfault.com/a/1190000007120459
本文为原创文章,转载请注明出处:http://www.cnblogs.com/acode/p/6385880.html
作者: Acode
出处: http://www.cnblogs.com/acode/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可留言咨询.