线程的建立、启动、销毁等是一个很是消耗资源的过程。引出线程池。java
目前线程池的共有六种建立方式。先来讲明常见的四种建立方式,本质都是经过改变构造函数的参数来建立不一样的线程的。最终调用的构造函数都是:面试
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
如下以demo分别说明线程池的建立:数组
ExecutorService threadPool = Executors.newCachedThreadPool(); for(int i=0;i<10;i++){ int temp = i; threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+","+temp); } }); } threadPool.shutdown();// 停掉线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { int temp = i; threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+","+temp); } }); } threadPool.shutdown();// 停掉线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3); threadPool.schedule(new Runnable() { @Override public void run() { System.out.println("我是定时线程,三秒后启动"); } },3, TimeUnit.SECONDS); // 第一个参数是任务,第二个参数是时间长度,第三个参数时间单位 threadPool.shutdown();// 停掉线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { threadPool.execute(new Runnable() { @Override public void run() { System.out.println("我是单线程线程"); } }); } threadPool.shutdown();// 停掉线程池
重点:几乎是面试必问的。缓存
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}
corePoolSize:核心线程数dom
maximumPoolSize:最大线程数ide
keepAliveTimea:线程空闲时间函数
unit:TimeUnit枚举类型的值,表明keepAliveTime时间单位,能够取下列值:this
workQueue:阻塞队列,用来存储等待执行的任务,决定了线程池的排队策略,有如下取值:线程
threadFactory:线程工厂,是用来建立线程的code
handler:线程拒绝策略。当建立的线程超出maximumPoolSize,且缓冲队列已满时,新任务会拒绝,有如下取值:
下图是线程池的执行流程:
流程说明:
用户提交任务,先到核心线程池,判断核心线程池是都已满;
如何核心线程池未满,线程任务执行;如何核心线程已满,走下一步;
进入线程缓存队列,判断缓存队列是否已满;
若是线程缓存队列已满,进入最大线程池;
若是最大线程池未满,建立线程任务;
若是最大线程池已满,则拒绝。
ExecutorService m = Executors.newWorkStealingPool(2);
1)建立一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传如并行级别参数,将默认为当前系统的CPU个数。
2)每一个线程维护本身的一个队列,任务执行结束了,就本身主动去取别的任务。
3)产生的都是守护线程。
4)不是 ThreadPoolExecutor 的扩展,是 ForkJoinPool 的扩展。
5)演示:
public class Main { public static void main(String[] args) throws Exception { // 设置并行级别为2,即默认每时每刻只有2个线程同时执行 ExecutorService m = Executors.newWorkStealingPool(2); for (int i = 1; i <= 10; i++) { final int count=i; m.submit(new Runnable() { @Override public void run() { Date now=new Date(); System.out.println("线程" + Thread.currentThread() + "完成任务:"+ count+" 时间为:"+ now.getSeconds()); try { Thread.sleep(1000);//此任务耗时1s } catch (InterruptedException e) { e.printStackTrace(); } } }); } while(true){ //主线程陷入死循环,来观察结果,不然是看不到结果的 } } }
该线程池的思想是,把运用递归的思想,将大的任务拆分红小的任务(能够根据业务需求来控制拆分的粒度)。下面以一个面试题演示:一个长度100万的数组,元素是一百之内的随机数,将各个元素相加。
public class ForkJoin { static int[] nums = new int[1000000]; static final int MAX_NUM = 50000; private static Random random = new Random(); static { for (int i = 0; i < nums.length; i++) { nums[i] = random.nextInt(100); } System.out.println(Arrays.stream(nums).sum()); // 传统的方式计算 } // 递归思想,不断将大任务分红小任务。 // RecursiveAction 无返回值;RecursiveTask 有返回值。 static class AddTask extends RecursiveAction{ int start,end; AddTask(int start, int end) { this.start = start; this.end = end; } @Override protected void compute() { if(end-start<=MAX_NUM){ long sum = 0L; for (int i = start; i < end; i++) { sum += nums[i]; } System.out.println("from+"+start+"to"+end+"="+sum); }else{ int middle =start + (end-start)/2; AddTask addTask = new AddTask(start,middle); AddTask addTask2 = new AddTask(middle,end); addTask.fork(); addTask2.fork(); } } } public static void main(String[] args) throws IOException { ForkJoinPool forkJoinPool = new ForkJoinPool(); AddTask addTask = new AddTask(0,nums.length); forkJoinPool.execute(addTask); System.in.read(); } }
该线程池的优点是,能够充分利用多cpu,多核cpu的优点,把一个任务拆分红多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成以后,再将这些执行结果合并起来便可。