在"Java多线程系列--“基础篇”01之 基本概念"中,我们介绍过,线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态。线程池也有5种状态;然而,线程池不同于线程,线程池的5种状态是:Running, SHUTDOWN, STOP, TIDYING, TERMINATED

线程池状态定义代码如下:

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;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;private static int ctlOf(int rs, int wc) { return rs | wc; }

说明:
ctl是一个AtomicInteger类型的原子对象。ctl记录了"线程池中的任务数量"和"线程池状态"2个信息。
ctl共包括32位。其中,高3位表示"线程池状态",低29位表示"线程池中的任务数量"。

状态 说明
RUNNING 对应的高3位值是111
SHUTDOWN 对应的高3位值是000
STOP 对应的高3位值是001
TIDYING 对应的高3位值是010
TERMINATED 对应的高3位值是011

线程池各个状态之间的切换如下图所示:

1. RUNNING

(01) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!道理很简单,在ctl的初始化代码中(如下),就将它初始化为RUNNING状态,并且"任务数量"初始化为0。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
2. SHUTDOWN

(01) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(02) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3. STOP

(01) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(02) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4. TIDYING

(01) 状态说明:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(02) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。

当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5. TERMINATED

(01) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(02) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

6. 拒绝策略介绍

线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施。

当任务添加到线程池中之所以被拒绝,可能是由于:
第一,线程池异常关闭。
第二,任务数量超过线程池的最大限制。

线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy

策略 说明
AbortPolicy 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常
CallerRunsPolicy 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务
DiscardOldestPolicy 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中
DiscardPolicy 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务

线程池默认的处理策略是AbortPolicy

7. 拒绝策略对比和示例

下面通过示例,分别演示线程池的4种拒绝策略。
(01) DiscardPolicy 示例
(02) DiscardOldestPolicy 示例
(03) AbortPolicy 示例
(04) CallerRunsPolicy 示例

7.1 DiscardPolicy 示例

import java.lang.reflect.Field;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;public class DiscardPolicyDemo {    private static final int THREADS_SIZE = 1;    private static final int CAPACITY = 1;    public static void main(String[] args) throws Exception {        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,                new ArrayBlockingQueue(CAPACITY));        // 设置线程池的拒绝策略为"丢弃"        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());        // 新建10个任务,并将它们添加到线程池中。        for (int i = 0; i < 10; i++) {            Runnable myrun = new MyRunnable("task-"+i);            pool.execute(myrun);        }        // 关闭线程池        pool.shutdown();    }}class MyRunnable implements Runnable {    private String name;    public MyRunnable(String name) {        this.name = name;    }    @Override    public void run() {        try {            System.out.println(this.name + " is running.");            Thread.sleep(100);        } catch (Exception e) {            e.printStackTrace();        }    }}

运行结果:

task-0 is running.task-1 is running.

结果说明:线程池pool的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),这意味着"线程池能同时运行的任务数量最大只能是1"。
线程池pool的阻塞队列是ArrayBlockingQueue,ArrayBlockingQueue是一个有界的阻塞队列,ArrayBlockingQueue的容量为1。这也意味着线程池的阻塞队列只能有一个线程池阻塞等待。
根据""中分析的execute()代码可知:线程池中共运行了2个任务。第1个任务直接放到Worker中,通过线程去执行;第2个任务放到阻塞队列中等待。其他的任务都被丢弃了!

7.2 DiscardOldestPolicy 示例

import java.lang.reflect.Field;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;public class DiscardOldestPolicyDemo {    private static final int THREADS_SIZE = 1;    private static final int CAPACITY = 1;    public static void main(String[] args) throws Exception {        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,                new ArrayBlockingQueue(CAPACITY));        // 设置线程池的拒绝策略为"DiscardOldestPolicy"        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());        // 新建10个任务,并将它们添加到线程池中。        for (int i = 0; i < 10; i++) {            Runnable myrun = new MyRunnable("task-"+i);            pool.execute(myrun);        }        // 关闭线程池        pool.shutdown();    }}class MyRunnable implements Runnable {    private String name;    public MyRunnable(String name) {        this.name = name;    }    @Override    public void run() {        try {            System.out.println(this.name + " is running.");            Thread.sleep(200);        } catch (Exception e) {            e.printStackTrace();        }    }}
运行结果:
task-0 is running.task-9 is running.

结果说明:将"线程池的拒绝策略"由DiscardPolicy修改为DiscardOldestPolicy之后,当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务,然后将被拒绝的任务添加到末尾。

7.3 AbortPolicy 示例

import java.lang.reflect.Field;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;import java.util.concurrent.RejectedExecutionException;public class AbortPolicyDemo {    private static final int THREADS_SIZE = 1;    private static final int CAPACITY = 1;    public static void main(String[] args) throws Exception {        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,                new ArrayBlockingQueue(CAPACITY));        // 设置线程池的拒绝策略为"抛出异常"        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());        try {            // 新建10个任务,并将它们添加到线程池中。            for (int i = 0; i < 10; i++) {                Runnable myrun = new MyRunnable("task-"+i);                pool.execute(myrun);            }        } catch (RejectedExecutionException e) {            e.printStackTrace();            // 关闭线程池            pool.shutdown();        }    }}class MyRunnable implements Runnable {    private String name;    public MyRunnable(String name) {        this.name = name;    }    @Override    public void run() {        try {            System.out.println(this.name + " is running.");            Thread.sleep(200);        } catch (Exception e) {            e.printStackTrace();        }    }}

(某一次)运行结果:

java.util.concurrent.RejectedExecutionException    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1774)    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:768)    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:656)    at AbortPolicyDemo.main(AbortPolicyDemo.java:27)task-0 is running.task-1 is running.

结果说明:将"线程池的拒绝策略"由DiscardPolicy修改为AbortPolicy之后,当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException。

7.4 CallerRunsPolicy 示例

import java.lang.reflect.Field;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;public class CallerRunsPolicyDemo {    private static final int THREADS_SIZE = 1;    private static final int CAPACITY = 1;    public static void main(String[] args) throws Exception {        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,                new ArrayBlockingQueue(CAPACITY));        // 设置线程池的拒绝策略为"CallerRunsPolicy"        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        // 新建10个任务,并将它们添加到线程池中。        for (int i = 0; i < 10; i++) {            Runnable myrun = new MyRunnable("task-"+i);            pool.execute(myrun);        }        // 关闭线程池        pool.shutdown();    }}class MyRunnable implements Runnable {    private String name;    public MyRunnable(String name) {        this.name = name;    }    @Override    public void run() {        try {            System.out.println(this.name + " is running.");            Thread.sleep(100);        } catch (Exception e) {            e.printStackTrace();        }    }}

运行结果:

task-2 is running.task-3 is running.task-4 is running.task-5 is running.task-6 is running.task-7 is running.task-8 is running.task-9 is running.task-0 is running.task-1 is running.

结果说明:将"线程池的拒绝策略"由DiscardPolicy修改为CallerRunsPolicy之后,当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到"线程池正在运行的线程"中取运行。


8 Callable 和 Future 简介

Callable 和 Future 是比较有趣的一对组合。当我们需要获取线程的执行结果时,就需要用到它们。Callable用于产生结果,Future用于获取结果。

8.1 Callable

Callable 是一个接口,它只包含一个call()方法。Callable是一个返回结果并且可能抛出异常的任务。

为了便于理解,我们可以将Callable比作一个Runnable接口,而Callable的call()方法则类似于Runnable的run()方法。

Callable的源码如下:

public interface Callable<V> {    V call() throws Exception;}

说明:从中我们可以看出Callable支持泛型。

8.2 Future

Future 是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

Future的源码如下:

public interface Future<V> {    // 试图取消对此任务的执行。    boolean     cancel(boolean mayInterruptIfRunning)    // 如果在任务正常完成前将其取消,则返回 true。    boolean     isCancelled()    // 如果任务已完成,则返回 true。    boolean     isDone()    // 如有必要,等待计算完成,然后获取其结果。    V           get() throws InterruptedException, ExecutionException;    // 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。    V             get(long timeout, TimeUnit unit)          throws InterruptedException, ExecutionException, TimeoutException;}

说明:Future用于表示异步计算的结果。它的实现类是FutureTask,在讲解FutureTask之前,我们先看看Callable, Future, FutureTask它们之间的关系图,如下:

说明:
(01) RunnableFuture是一个接口,它继承了Runnable和Future这两个接口。RunnableFuture的源码如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {    void run();}

(02) FutureTask实现了RunnableFuture接口。所以,我们也说它实现了Future接口。

9 示例和源码分析

我们先通过一个示例看看Callable和Future的基本用法,然后再分析示例的实现原理。

import java.util.concurrent.Callable;import java.util.concurrent.Future;import java.util.concurrent.Executors;import java.util.concurrent.ExecutorService;import java.util.concurrent.ExecutionException;class MyCallable implements Callable {    @Override     public Integer call() throws Exception {        int sum    = 0;        // 执行任务        for (int i=0; i<100; i++)            sum += i;        //return sum;         return Integer.valueOf(sum);    } }public class CallableTest1 {    public static void main(String[] args)         throws ExecutionException, InterruptedException{        //创建一个线程池        ExecutorService pool = Executors.newSingleThreadExecutor();        //创建有返回值的任务        Callable c1 = new MyCallable();        //执行任务并获取Future对象         Future f1 = pool.submit(c1);        // 输出结果        System.out.println(f1.get());         //关闭线程池         pool.shutdown();     }}

运行结果:

4950
结果说明:在主线程main中,通过newSingleThreadExecutor()新建一个线程池。接着创建Callable对象c1,然后再通过pool.submit(c1)将c1提交到线程池中进行处理,并且将返回的结果保存到Future对象f1中。然后,我们通过f1.get()获取Callable中保存的结果;最后通过pool.shutdown()关闭线程池。

9.1 submit()

submit()在java/util/concurrent/AbstractExecutorService.java中实现,它的源码如下:

public Futuresubmit(Callable task) {    if (task == null) throw new NullPointerException();    // 创建一个RunnableFuture对象    RunnableFuture ftask = newTaskFor(task);    // 执行“任务ftask”    execute(ftask);    // 返回“ftask”    return ftask;}

说明:submit()通过newTaskFor(task)创建了RunnableFuture对象ftask。它的源码如下:

protected RunnableFuturenewTaskFor(Callable callable) {    return new FutureTask(callable);}

9.2. FutureTask的构造函数

FutureTask的构造函数如下:

public FutureTask(Callable callable) {    if (callable == null)        throw new NullPointerException();    // callable是一个Callable对象    this.callable = callable;    // state记录FutureTask的状态    this.state = NEW;       // ensure visibility of callable}

9.3 FutureTask的run()方法

我们继续回到submit()的源码中。

在newTaskFor()新建一个ftask对象之后,会通过execute(ftask)执行该任务。此时ftask被当作一个Runnable对象进行执行,最终会调用到它的run()方法;ftask的run()方法在java/util/concurrent/FutureTask.java中实现,源码如下:

public void run() {    if (state != NEW ||        !UNSAFE.compareAndSwapObject(this, runnerOffset,                                     null, Thread.currentThread()))        return;    try {        // 将callable对象赋值给c。        Callable c = callable;        if (c != null && state == NEW) {            V result;            boolean ran;            try {                // 执行Callable的call()方法,并保存结果到result中。                result = c.call();                ran = true;            } catch (Throwable ex) {                result = null;                ran = false;                setException(ex);            }            // 如果运行成功,则将result保存            if (ran)                set(result);        }    } finally {        runner = null;        // 设置“state状态标记”        int s = state;        if (s >= INTERRUPTING)            handlePossibleCancellationInterrupt(s);    }}

说明:run()中会执行Callable对象的call()方法,并且最终将结果保存到result中,并通过set(result)将result保存。
之后调用FutureTask的get()方法,返回的就是通过set(result)保存的值。

terminated 线程_Java【多线程系列】JUC线程池—2. 原理(二)、Callable和Future相关推荐

  1. Java多线程系列--“JUC线程池”06之 Callable和Future

    转载自  Java多线程系列--"JUC线程池"06之 Callable和Future Callable 和 Future 简介 Callable 和 Future 是比较有趣的一 ...

  2. Java多线程系列 JUC线程池01 线程池框架

    转载  http://www.cnblogs.com/skywang12345/p/3509903.html 为什么引入Executor线程池框架 new Thread()的缺点 1. 每次new T ...

  3. Java多线程系列 JUC线程池05 线程池原理解析(四)

    转载 http://www.cnblogs.com/skywang12345/p/3544116.html  https://blog.csdn.net/programmer_at/article/d ...

  4. java futher多线程_Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap

    概要 本章对Java.util.concurrent包中的ConcurrentSkipListMap类进行详细的介绍.内容包括: ConcurrentSkipListMap介绍 ConcurrentS ...

  5. java 镶嵌创建线程_Java多线程——之一创建线程的四种方法

    1.实现Runnable接口,重载run(),无返回值 package thread; public class ThreadRunnable implements Runnable { public ...

  6. java 线程强制停止线程_java多线程之停止线程

    在多线程开发中停止线程是非常重要的技术点. 停止线程在Java语言中并不像break语句那样干脆.须要一些技巧性的处理. 一.  异常法 採用异常法来停止一个线程.首先我们须要了解一下两个方法的使用方 ...

  7. Java多线程系列--“JUC锁”05之 非公平锁

    转载自:http://www.cnblogs.com/skywang12345/p/3496651.html点击打开链接 概要 前面两章分析了"公平锁的获取和释放机制",这一章开始 ...

  8. Java多线程系列---“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下: 01. Java多线程系列--"JUC锁"01之 框架 02. Java多线程系列--"JUC锁 ...

  9. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对"公平锁"的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括: 基本概念 ReentrantLock数据结构 参考代码 获取公平锁(基于JDK1.7.0 ...

  10. Java多线程系列--“JUC原子类”01之 框架

    2019独角兽企业重金招聘Python工程师标准>>> Java多线程系列--"JUC原子类"01之 框架 根据修改的数据类型,可以将JUC包中的原子操作类可以分 ...

最新文章

  1. XXE漏洞检测及代码执行过程
  2. vue 设置proxyTable - pathRewrite无效
  3. C# 对接微信支付时生成符合 RFC3339 标准的日期时间字符串
  4. 第 13 章 装饰模式【Decorator Pattern】
  5. 亲测可用|奥维互动地图加载谷歌地图等图源的方法
  6. cadvisor 离线安装
  7. Mac - 输入法卡顿
  8. xp开机黑屏故障分析
  9. ABAP 生产订单的创建与修改函数
  10. Unity 下拉列表OnValueChanged
  11. 《数据结构与算法之二叉平衡树(AVL)》
  12. R语言绘制频率直方图
  13. VR行业中的三维扫描技术应用
  14. 在软件部署中使用 strace 进行调试 | Linux 中国
  15. hacksudo-Thor(Vulnhub)
  16. 单片机怎么学?有必要参加单片机培训班吗?
  17. Python--Flask在使用 SQLAlchemy出现'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
  18. 牛客小白月赛15A 斑羚飞渡
  19. 读《编写高质量iOS与OS X代码的52个有效方法》
  20. 大学计算机专业,哪些课程是必须学好的重点?

热门文章

  1. sql中当前日期加2个月_一文搞定Mysql日期时间函数
  2. 巨坑!同事离职前一天,偷偷将一个470欧的色环电阻串进我的电源线中......
  3. 来,看看这20个常用的宏定义!
  4. HDLBits答案(11)_Verilog计数器
  5. html5 绘制图形,HTML5绘制几何图形
  6. mysql为什么使用b 树作为索引_为什么Mysql用B+树作为索引
  7. 联想微型计算机报价,联想电脑一体机报价
  8. 字节跳动python面试题_字节跳动2019两道春招笔试题python解法
  9. linux prc 时区,授时时区问题解决
  10. ei会议论文录用但不参加会议_会议论文投稿可以不参会吗