一、线程池简介
线程池就是预先创建好多n个空闲线程,节省了每次使用线程时都要去创建的时间,使用时只要从线程池中取出,用完之后再还给线程池。就像现在的共享经济一样,需要的时候只要去“借”,用完之后只需还回去就行。“池”的概念都是为了节省时间而创建的。
二、Executor
Java SE5增加了juc包来简化并发编程,而juc包中的Executor执行器来管理Thread对象。Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许我们管理异步执行的任务,而无须显示的管理线程的生命周期,是启动线程的优先选择。Executors 是Executor、ExecutorService、ScheduledExecutorService以及ThreadFactory、Callable的工厂及实用方法
三、线程池使用
1、newCachedThreadPool
newCachedThreadPool 会为每个任务都创建一个线程,如果有空闲线程的话也会重新使用空闲线程,如果线程没有被使用的话会在60s之后终止并从线程池中移除。它在回收旧线程时会停止创建新线程。
例子:
public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int count = i; executorService.execute(new Runnable() { @Override public void run() { System.out.printf("thread_%d_start%n" ,count); } }); } executorService.shutdown(); //用完记得关闭,要不然会一直挂起 } }
2、newFixedThreadPool
newFixedThreadPool 创建了一个固定数量的线程池,它重用了固定数量的线程操作一个无界队列,无论什么时候,它最多只能运行固定数量的运行中任务,当所有线程都处于活跃状态,如果有新的任务要添加进来的话,只能在队列中等待,直到有空闲线程。newFixedThreadPoll会一个运行,除非显示的调用shutdown方法。
public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int count = i; executorService.execute(() -> { System.out.printf("thread_%d_start%n" ,count); try { Thread.sleep(3*1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂 } }
休眠3s 是为了更能看到效果,你会发现每次会先执行5个线程,结束之后会继续执行剩余5个。
3、newSingleThreadExecutor
newSingleThreadExecutor 使用单个线程操作了一个无界队列创建了一个Executor,它保证了任务执行的有序性。 如果向newSingleThreadExecutor中提交多个任务的话,每个任务都会保证在下个任务开始之前结束,所有的任务都将使用相同的线程
public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int count = i; executorService.execute(() -> { System.out.printf("thread_%d_start%n" ,count); try { Thread.sleep(3*1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂 } }
执行结果:
线程按照指定顺序有序的执行,说明newSingleThreadExecutor在维护着一个有序队列。
4、newScheduledThreadPool
创建了一个能够延迟或周期性执行任务的线程池。
例子:
1、延时
public class ThreadPoolDemo { public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); for (int i = 0; i < 3; i++) { final int count = i; executorService.schedule(() -> { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread_"+count); }, 3 , TimeUnit.SECONDS); } System.out.println("------------"); executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂 } }
线程启动后 会延迟3s后再执行。
2、周期性执行
public class ThreadPoolDemo { public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); for (int i = 0; i < 3; i++) { final int count = i; executorService.scheduleAtFixedRate(() -> { System.out.println("thread_"+count); try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }, 1 , 3 , TimeUnit.SECONDS); } System.out.println("------------"); // executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂 } }
线程池不关闭,线程延迟1s执行后,会每隔3s周期性执行。
四、线程池的关闭
线程池关闭有两个方法:
它们的原理都是通过遍历线程池中的工作任务,然后通过调用线程的interrupt方法来中断线程,如果无法响应中断的任务可能永远无法关闭。
1、void shutdown():shutdown方法会将线程池状态设置为SHUTDOWN,然后中断线程池中没有正在执行的线程,新添加进来的任务将不会再被接受。
2、List<Runnable> shutdownNow():shutdownNow方法会将线程池状态设置为STOP,然后尝试停止正在执行或者暂停任务的线程,并返回等待执行任务的列表。