在java.util.concurrent包下,提供了一系列与线程池相关的类。合理的使用线程池,可以带来多个好处:
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
线程池可以应对突然大爆发量的访问,通过有限个固定线程为大量的操作服务,减少创建和销毁线程所需的时间。
我们一般通过Executors类下的四个成员函数创建相应的线程池:
?
1 2 3 4 5 6 7 8 9 10 11 12 | <code class = "hljs cs" > //创建一个定时任务的线程池 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool( 4 ); //创建单线程的线程池 ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); //创建缓存线程池(重用先前的线程) ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); //创建一个带有固定线程的线程池 ExecutorService threadPool = Executors.newFixedThreadPool( 4 ); </code> |
我们可以从源码看出,上面四个方法会调用ThreadPoolExecutor的构造方法创建线程池。
?
1 2 3 4 5 | <code class = "hljs cs" > public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>()); }</runnable></code> |
创建一个ThreadPoolExecutor线程池一般需要以下几个参数:
corePoolSize(线程池的基本大小):
当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
maximumPoolSize(线程池最大大小):
线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
keepAliveTime(线程活动保持时间):
线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
TimeUnit(线程活动保持时间的单位):
可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS)等。
workQueue(任务队列):
用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue
threadFactory:
用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
handler(饱和策略):
当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。
我们尽量优先使用Executors提供的静态方法来创建线程池,如果Executors提供的方法无法满足要求,再自己通过ThreadPoolExecutor类来创建线程池。
有两种方式:
① 我们可以使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute方法输入的任务是一个Runnable类的实例。
?
1 2 3 4 5 6 | <code class = "hljs java" >threadsPool.execute( new Runnable() { @Override public void run() { // TODO Auto-generated method stub } });</code> |
② 使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。
?
1 2 3 4 5 6 7 8 9 10 11 | <code class = "hljs javascript" >Future<object> future = executor.submit(harReturnValuetask); try { Object s = future.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); }</object></code> |
我们可以通过调用线程池(ExecutorService的对象)的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
public void run() { try { Runnable task = firstTask; firstTask = null; while (task != null || (task = getTask()) != null) { runTask(task); task = null; } } finally { workerDone(this); } }
通过继承线程池并重写线程池的beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。
示例一:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | <code class = "hljs java" > public class DifferentKindsThreadPool { public static void main(String[] args) { // displayScheduledThreadPool(); // displaySingleThreadPool(); // displayCachedThreadPool(); displayThreadPool(); } /** * 创建一个定时任务的线程池 */ public static void displayScheduledThreadPool() { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool( 4 ); //它可以向固定线程池一样执行任务 distributeTaskForThreadPool(scheduledThreadPool); //这是它的特殊之处,可以定时任务 scheduledThreadPool.schedule( new Runnable() { @Override public void run() { System.out.println( "开始执行任务1" ); } }, 5 , TimeUnit.SECONDS); //每隔2秒再次重新执行任务 scheduledThreadPool.scheduleAtFixedRate( new Runnable() { @Override public void run() { System.out.println( "开始执行任务2" ); } }, 5 , 3 , TimeUnit.SECONDS); } /** * 创建只有一个线程的线程池,如果线程终止, * 他将会创建一个新的线程加入到池子中,这 * 个线程池会保证池子中始终有一个线程 */ public static void displaySingleThreadPool() { ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); distributeTaskForThreadPool(singleThreadPool); } /** * 创建一个可根据需要创建线程的线程池,但是 * 当先前创建的线程可得到时就会重用先前的线 * 程,如果不存在可得到的线程,一个新的线程 * 将被创建并被加入到池子中。60秒没有被用到 * 的线程将被终止并从缓存中移除 */ public static void displayCachedThreadPool() { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); distributeTaskForThreadPool(cachedThreadPool); } /** * 创建一个带有固定线程的线程池 */ public static void displayThreadPool() { // 创建一个带有4个固定线程的线程池 ExecutorService threadPool = Executors.newFixedThreadPool( 4 ); distributeTaskForThreadPool(threadPool); } /** * 为线程池分配8个任务,使其驱动 * @param threadPool */ public static void distributeTaskForThreadPool(ExecutorService threadPool) { // 让线程池驱动8个任务 for ( int i = 1 ; i <= 8 ; i++) { // 由于内部类里面不能放一个非final的变量,所以我把i的值赋予task final int task = i; threadPool.execute( new Runnable() { @Override public void run() { System.out.println( "我是" + Thread.currentThread().getName() + "," + "拿到了第" + task + "个任务,我开始执行了" ); } }); } } } </code> |
示例二:
?
1 2 3 4 5 6 7 8 9 | <code class = "hljs cs" > public class BankCount { public synchronized void addMoney( int money){ //存钱 System.out.println(Thread.currentThread().getName() + ">存入:" + money); } public synchronized void getMoney( int money){ //取钱 System.out.println(Thread.currentThread().getName() + ">取钱:" + money); } }</code> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <code class = "hljs cs" > public class BankTest { public static void main(String[] args) { final BankCount bankCount = new BankCount(); ExecutorService executor = Executors.newFixedThreadPool( 10 ); executor.execute( new Runnable() { //存钱线程 @Override public void run() { int i = 5 ; while (i-- > 0 ){ bankCount.addMoney( 200 ); try { Thread.sleep( 500 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Future<!--?--> future = executor.submit( new Runnable() { //取钱线程 @Override public void run() { int i = 5 ; while (i-- > 0 ){ try { Thread.sleep( 500 ); } catch (InterruptedException e) { e.printStackTrace(); } bankCount.getMoney( 200 ); } } }); try { Object res = future.get(); System.out.println(res); } catch (InterruptedException e) { // 处理中断异常 e.printStackTrace(); } catch (ExecutionException e) { // 处理无法执行任务异常 e.printStackTrace(); } finally { // 关闭线程池 executor.shutdown(); } } }</code> |