线程池原理(基于jdk1.8.0_20)

作用

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

使用

通过Executors类,提供四种线程池

方式 特点
newCachedThreadPool 可缓存线程池,线程池长度超过处理需要,可回收线程,线程池为无限大,当执行第二个任务的时候,第一个任务已经完成,会复用第一个任务的线程,而不用重新创建
newFixedThreadPool 定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool 定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor 单例线程池,用唯一的工作线程执行任务,保证所有任务按照指定顺序执行(FIFO或者LIFO)
public class Task extends Thread{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class TestCachedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-5 is running
        //pool-1-thread-2 is running
        //pool-1-thread-4 is running
        //pool-1-thread-3 is running
        //必须显式结束,不然程序永远不会结束
        executorService.shutdown();
    }
}

这个看起来好像没有用到线程池,其实是因为没有可复用的线程,所以就一直创建新的线程了

public class TestFixedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-2 is running
        //pool-1-thread-1 is running
        //pool-1-thread-2 is running
        //pool-1-thread-1 is running
        executorService.shutdown();
    }
}
public class TestScheduledThreadPool {

    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        //任务,第1次任务延迟的时间,2次任务间隔时间,时间单位
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("task 1 " + System.currentTimeMillis());
            }
        }, 1, 5, TimeUnit.SECONDS);
        //两者互不影响
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("task 2 " + System.currentTimeMillis());
            }
        }, 1, 2,TimeUnit.SECONDS);
        //task 1 1521949174111
        //task 2 1521949174112
        //task 2 1521949176106
        //task 2 1521949178122
        //task 1 1521949179104
        //task 2 1521949180114
    }
}
public class TestSingleThreadExecutor {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            Task task = new Task();
            executorService.execute(task);
        }
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        //pool-1-thread-1 is running
        executorService.shutdown();
    }
}

源码

这里写图片描述

状态

状态 含义
RUNNING 接受新任务,并且处理进入队列的任务 -536870912
SHUTDOWN 不接受新任务,处理进入队列的任务 0
STOP 不接受新任务,不处理进入队列的任务,并且中断正在执行的任务 536870912
TIDYING 所有任务执行完成,workerCount为0。线程转到了状态TIDYING会执行terminated()钩子方法 1073741824
TERMINATED terminated()已经执行完成 1610612736

这里写图片描述

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

AtomicInteger类型的ctl代表了ThreadPoolExecutor的控制状态,是一个复合类型的变量,借助高低位包装了2个概念
1. runState 线程池运行状态,占据ctrl的高三位
2. workerCount 线程池中当前活动的线程数量,占据ctl的低29位

COUNT_BITS代表了workerCount所占的位数,即29,而CAPACITY表示线程池理论的最大活动线程数量,即536870911
这里写图片描述
0在Java底层是由32个0表示的,无论左移多少位,还是32个0,即SHUTDOWN的值为0,TIDYING则是高三位为010,低29为0

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,2低位的9个0,然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState。

private static int ctlOf(int rs, int wc) { return rs | wc; }

传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值的int,将runState和workerCount做按位或,即用runState的高3位,workerCount的低29位填充的数字

构造函数

ThreadPoolExecutor一共有4个构造函数,但最后调用的都是如下构造函数

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;
}
参数 含义
corePoolSize 核心线程池大小
maximumPoolSize 线程池最大容量大小
keepAliveTime 线程池空闲时,除核心线程以外的线程存活的时间
TimeUnit 线程活动保持时间的单位
BlockingQueue<Runnable> 任务队列,用于保存等待执行的任务的阻塞队列
ThreadFactory 用于设置线程的工厂
RejectedExecutionHandler 饱和策略

这里简单说一下corePoolSize和maximumPoolSize,可以进行如下类比学习,corePoolSize=公司的基本人员,maximumPoolSize=公司的基本人员+外包人员。corePoolSize,保持存活的工作线程的最小数目,当小于corePoolSize时,会直接启动新的一个线程来处理任务,而不管线程池中是否有空闲线程

RejectedExecutionHandler是一个接口,有4个实现类,对应4种处理策略,这4个实现类是ThreadPoolExecutor的静态内部类

策略
AbortPolicy 丢弃任务,抛运行时异常
CallerRunsPolicy 执行任务
DiscardPolicy 忽视,什么都不会发生
DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

execute执行流程

  1. 首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
  2. 其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
  3. 最后线程池判断整个线程池是否已满(即线程数是否小于线程池最大容量)?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

这里写图片描述

retry使用
break retry 跳到retry处,且不再进入循环
continue retry 跳到retry处,且再次进入循环

public static void main(String[] args) {
    breakRetry();
    continueRetry();
}

private static void breakRetry() {
    int i = 0;
    retry:
    for (; ; ) {
        System.out.println("start");
        for (; ; ) {
            i++;
            if (i == 4)
                break retry;
        }
    }
    //start 进入外层循环
    //4
    System.out.println(i);
}

private static void continueRetry() {
    int i = 0;
    retry:
    for(;;) {
        System.out.println("start");
        for(;;) {
            i++;
            if (i == 3)
                continue retry;
            System.out.println("end");
            if (i == 4)
                break retry;
        }
    }
    //start 第一次进入外层循环
    //end i=1输出
    //end i=2输出
    //start 再次进入外层循环
    //end i=4输出
    //4 最后输出
    System.out.println(i);
}

submit和execute的联系和区别

execute这个方法在Executor接口中定义,并且直接由ThreadPoolExecutor来实现

public interface Executor {
    void execute(Runnable command);
}

参考博客

Java线程池的分析和使用
[1]http://ifeve.com/java-threadpool/
[2]https://www.jianshu.com/p/ade771d2c9c0
[3]https://blog.csdn.net/lipeng_bigdata/article/details/51232266
[4]https://blog.csdn.net/qq_19431333/article/details/59030892
四种Java线程池用法解析
[5]https://www.cnblogs.com/ruiati/p/6134131.html

猜你喜欢

转载自blog.csdn.net/zzti_erlie/article/details/79676357