一:线程池的用途
平时的业务中,如果要使用多线程,那么我们会在业务开始前创建线程,业务结束后,销毁线程。但是对于业务来说,线程的创建和销毁是与业务本身无关的,只关心线程所执行的任务。因此希望把尽可能多的cpu用在执行任务上面,而不是用在与业务无关的线程创建和销毁上面。而线程池则解决了这个问题,线程池的作用就是将线程进行复用。
二:java并发包提供的线程池
1:线程池的类(Executors类)
类图
newFixedThreadPool 固定数量的线程池,线程池中的线程数量是固定的,不会改变。
newSingleThreadExecutor 单一线程池,线程池中只有一个线程。执行完成一个再执行另外一个,其实有点把多线程变成了单线程
newCachedThreadPool 缓存线程池,线程池中的线程数量不固定,会根据需求的大小进行改变。它的线程回自动释放默认时间是60s
newScheduledThreadPool 计划任务调度的线程池,用于执行计划任务,比如每隔5分钟怎么样,
newWorkStealingPool 创建一个 work-stealing 线程池,使用目前机器上可用的处理器作为它的并行级别。 jdk1.8新增的
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); } public static ExecutorService newWorkStealingPool() { return new ForkJoinPool (Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
ThreadPoolExecutor 构造函数中参数的含义。
corePoolSize 线程池中核心线程数的数目
maximumPoolSize 线程池中最多能容纳多少个线程
keepAliveTime 当现在线程数目大于corePoolSize时,超过keepAliveTime时间后,多出corePoolSize的那些线程将被终结。
unit keepAliveTime的单位
workQueue 当任务数量很大,线程池中线程无法满足时,提交的任务会被放到阻塞队列中,线程空闲下来则会不断从阻塞队列中取数据。
FixedThreadPool,它的线程的核心数目和最大容纳数目都是一样的,以至于在工作期间,并不会创建和销毁线程。当任务数量很大,线程池中的线程无法满足时,任务将被保存到LinkedBlockingQueue中,而LinkedBlockingQueue的大小是Integer.MAX_VALUE。这就意味着,任务不断地添加,会使内存消耗越来越大。
CachedThreadPool,它的核心线程数量是0,最大容纳数目是Integer.MAX_VALUE,它的阻塞队列是SynchronousQueue,这是一个特别的队列,它的大小是0。由于核心线程数量是0,所以必然要将任务添加到SynchronousQueue中,这个队列只有一个线程在从中添加数据,同时另一个线程在从中获取数据时,才能成功。独自往这个队列中添加数据会返回失败。当返回失败时,则线程池开始扩展线程,这就是为什么CachedThreadPool的线程数目是不固定的。当60s该线程仍未被使用时,线程则被销毁。
相关演示代码
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorsDome { public static void main(String[] args) { //ExecutorService executorService = Executors.newCachedThreadPool();//默认是60s 的线程时间 ExecutorService executorService = Executors.newFixedThreadPool(10);//固定数量的线程池,线程池中的线程数量是固定的,不会改变。 // ExecutorService executorService = Executors.newSingleThreadExecutor();//线程池中只有一个线程,执行完成一个在执行另外一个,其实有点把多线程变成了单线程 //ExecutorService executorService =Executors.newScheduledThreadPool(6); // ExecutorService executorService=Executors.newWorkStealingPool(); executorService.execute(new ThreadPools(1)); executorService.execute(new ThreadPools(2)); executorService.execute(new ThreadPools(3)); executorService.execute(new ThreadPools(4)); executorService.execute(new ThreadPools(5)); executorService.submit(new ThreadPools(6)); } } class ThreadPools implements Runnable { private int i=0; public ThreadPools(int i){ this.i=i; } public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("ThreadPools:"+i); } }
==============================
定时的demo
public class ScheduledExecutorServiceDemo { public static void main(String[] args) throws Exception, ExecutionException { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); ScheduledFuture scheduledFuture = executorService.schedule(new Callable() { public String call() throws Exception { return "call"; } }, 10, TimeUnit.SECONDS); System.out.println(scheduledFuture.get()); executorService.shutdown(); } }
结果是10s后返回"call"的结果
2:拒绝(饱和)策略
ThreadPoolExecutor的构造函数里面有一个参数是RejectedExecutionHandler,它是一个接口实现类有:AbortPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy,这这个分别代表了4种策略
AbortPolicy:如果不能接受任务了,则抛出异常。默认拒绝策略是它
CallerRunsPolicy:如果不能接受任务了,则让调用的线程去完成。
DiscardOldestPolicy:如果不能接受任务了,则丢弃最老的一个任务,由一个队列来维护。
DiscardPolicy:如果不能接受任务了,则丢弃任务。
当然我们也可以自己实现RejectedExecutionHandler接口来自己定义拒绝策略。
3:ForkJoin
fork/join(jdk1.7新增)框架是ExecutorService接口的一种具体实现,目的是为了帮助你更好地利用多处理器带来的好处。它是为那些能够被递归地拆解成子任务的工作类型量身设计的。其目的在于能够使用所有可用的运算能力来提升你的应用的性能。类似于ExecutorService接口的其他实现,fork/join框架会将任务分发给线程池中的工作线程。fork/join框架的独特之处在与它使用工作窃取(work-stealing)算法。完成自己的工作而处于空闲的工作线程能够从其他仍然处于忙碌(busy)状态的工作线程处窃取等待执行的任务。fork/join框架的核心是ForkJoinPool类,它是对AbstractExecutorService类的扩展。ForkJoinPool实现了工作偷取算法,并可以执行ForkJoinTask任务。
他的思想可以用一张图来了解
演示代码
import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveTask; //1*1+2*2+3*3....100*100 public class ForkJoinPoolDemo { public static void main(String[] args) { MyTask mt = new MyTask(1); ForkJoinPool forkJoinPool = new ForkJoinPool(); Future result = forkJoinPool.submit(mt); try { System.out.println(result.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } forkJoinPool.shutdown(); A.a(); } } //ForkJoin算法 class MyTask extends RecursiveTask { int i; public MyTask(int i){ this.i = i; } @Override protected Integer compute() { if (i >= 100) { return i * i; } MyTask newTask2 = new MyTask(i + 1); newTask2.fork(); return i * i + newTask2.join(); } } //普通算法 class A { public static void a() { int j=0; for (int i = 1; i <= 100; i++) { j=j+(i*i); } System.out.println(j); } }
以上两种算法的结果是一样的都是 338350。更深入的了解请网上超资料。