Java教程

java线程及线程池-使用

本文主要是介绍java线程及线程池-使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

概览

线程是进程中的一个单一的顺序控制流,因此单个进程可以拥有多个并发执行的任务。

线程模式为编程带来了便利,它简化了在单一程序中同时交织在一起的多个操作的处理。在使用线程时,CPU将轮流给每个任务分配其占用时间。

定义任务 - Runnable接口(为什么不叫task)

线程可以驱动任务,因此你需要一种描述任务的方式。这可以由Runnable接口来提供。要想定义任务,只需实现Runnable接口并编写run()方法,使得该任务可以执行你的命令。

package com.fallsown.fallsownthread;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
public class LiftOff implements Runnable{
    protected int countDown = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    public LiftOff(){}
    public LiftOff(int countDown){
        this.countDown = countDown;
    }
    public String status(){
        return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + ").";
    }
    public void run(){
        while(countDown-- > 0){
            System.out.println(status());
            Thread.yield();
        }
    }
}

标识符id可以用来区分任务的多个实例,它是final的,因为它一旦被初始化之后就不希望被修改。

任务的run()方法通常总会有某种形式的循环,使得任务可以一直运行下去直到不需要,所以要设定跳出循环的条件(由一种选择是直接从run()返回)

通常run()被写成无线循环的形式,这意味着除非由某个条件使得run()终止,否则它将永远运行下去。

在run()中对静态方法Thread.yield()的调用是对线程调度器(Java线程机制的一部分,可以将cpu从一个线程转移给另一个线程)的一种建议。它在声明“我已经执行完声明周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机。”

这完全是选择性的,但是这里使用它会在这些实例中产生更加有趣的输出。你更有可能会看到任务换进换出的证据。

在下面的实例中,这个任务的run()不是由单独的线程驱动的,它是在main()中直接调用的
public class MainThread{
    public static void main(String[] args){
        LiftOff launch = new LiftOff();
        launch.run();
    }
}

将Runnable导出一个类,它必须具有run()方法,但是这个方法没有特殊之处--它不会产生任何内在的线程能力。要实现线程行为,就必须显式将一个任务附在线程上。

Thread类

将Runnable对象转变为工作任务的传统方式,是把它提交给一个Thread构造器,下面的示例展示了如何使用Thread来驱动LiftOff对象

package com.fallsown.fallsownthread;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
public class BasicThreads {
    public static void main(String[] args){
        Thread t = new Thread(new LiftOff());
        t.start();
        System.out.println("Waiting for LiftOff");
    }
}

Thread构造器只需要一个Runnable对象,调用Thread对象的start()方法为该线程执行必须的初始化操作,然后调用Runnable的run()方法,以便在这个新线程中启动该任务。

尽管start()看起来是产生了一个对长期运行方法的调用。但是从输出中可以看到,start()迅速返回了。因为Waiting for LiftOff消息在倒计时完成之前就出现了。

实际上,你产生的是对LiftOff.run()的方法调用,并且这个方法还没有完成,但是因为LiftOff.run()是由不同的线程执行的,因此你任然可以执行main()线程中的其他操作。(这种能力并不局限于main()线程,任何线程都可以启动另一个线程)。main()和LiftOff.run()是程序中与其他线程“同时”执行的代码。

使用Executor

Java SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象。LiftOff对象知道如何运行具体的任务,与命令设计模式一样,它暴露了要执行的单一方法。

ExecutorService(具有服务生命周期的Executor,例如关闭)只得到如何构建恰当的上下文来执行Runnable对象。

在下面的示例中,CachedThreadPool将为每个任务都创建一个线程,注意ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor类型。

package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
public class CachedThreadPool {
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++){
            exec.execute(new LiftOff());
        }
        exec.shutdown();
    }
}

非常常见的是单个Executor被用来创建和管理系统中的所有任务。

对shutdown()方法的调用可以防止新任务被提交给这个Executor,当前线程(在本例中,即驱动main()的线程)将继续运行在shutdown()被调用之前提交的任务。这个程序将在Executor中所有任务完成之后尽快退出。

可以很容易地将前面示例中地CachedThreadPool替换为不同类型地Executor,FixedThreadPool使用了有限地线程集来执行所提交的任务。

package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
public class CachedThreadPool {
    public static void main(String[] args){
        ExecutorService exec = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 5; i++){
            exec.execute(new LiftOff());
        }
        exec.shutdown();
    }
}

有了FixedThreadPool,就可以一次性预先支付代价高昂的线程分配。因而可以限制线程数量。

在事件驱动的系统中,需要线程的事件处理器,通过直接从池中获取线程,也可以如你所愿地尽快得到服务。你不会滥用可获得地资源,因为FixedThreadPool使用Thread对象地数量是有界地。

几种类型的区别

CachedThreadPool在程序执行过程中通常会创建与所需数量相同地线程,然后再它回收旧线程时停止创建新线程。因此它是合理地Executor的首选。只有当这种方式引发问题的时候,你才需要切换到FixedThreadPool

SingleThreadExecutor就像是线程数量为1的FixedThreadPool,这对于你希望在另一个线程中连续运行的任何事务(长期存活的任务)来说,都是很有用的。例如监听进入的套接字连接的任务,它对于希望在线程中运行的短任务也同样很方便。例如,更新本地或远程日志的小任务,或者是事件分发线程。

如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束。所有的任务将使用相同的线程。

SingleThreadExecutor会序列化所有提交给它的任务,并会维护它自己(隐藏)的悬挂任务队列

它还提供了一种重要并发机制,其他线程不会(即没有两个线程会)被并发调用。这会改变任务的加锁需求。

从任务中产生返回值--Callable接口

Runnable是执行工作的独立任务,但是它不返回任何值。如果你希望任务在完成时能返回一种值,那么可以实现Callable接口而不是Runnable接口

在Java SE5中引入的Callabel是一种具有类型参数的泛型。它的类型参数表示的是从方法call()(而不是run())中返回的值,并且必须使用ExecutorService.submit()方法调用它,下面是一种简单示例:

package com.fallsown.fallsownthread;

import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
class TaskWithResult implements Callable<String>{
    private int id;
    public TaskWithResult(int id){
        this.id = id;
    }
    public String call(){
        return "result of TaskWithResult " + id;
    }
}

public class CallableDemo {
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool();
        ArrayList<Future<String>> results = new ArrayList<>();
        for(int i = 0; i < 10; i++){
            results.add(exec.submit(new TaskWithResult(i)));
        }
        for(Future<String> fs : results)
            try {
                System.out.println(fs.get());
            }catch (InterruptedException e){
                System.out.println(e);
                return;
            }catch (ExecutionException e){
                System.out.println(e);
            }finally {
                exec.shutdown();
            }
    }
}

submit()方法会产生Future对象,它用Callable返回结果的特定类型进行了参数化。你可以用isDone()方法来查询Future是否已经完成。

当任务完成时,它具有一个结果,你可以调用get()方法来获取该结果,你也可以不用isDone()进行检查就直接调用get(),在这种情况下get()将会阻塞,直至结果准备就绪。

你还可以在试图调用get()来获取结果之前,先调用具有超时的get(),或者调用isDone()来查看任务是否完成

休眠

影响任务行为的一种简单方法就是调用sleep(),这将使任务终止执行给定的事件,在LiftOff类中,要是把对yield()的调用换成是调用sleep(),将得到如下结果。

package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/13
 */
public class SleepingTask extends LiftOff {
    @Override
    public void run(){
        try{
            while (countDown-- > 0){
                System.out.println(status());
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch (InterruptedException e){
            System.out.println("Interrupted");
        }
    }
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++){
            exec.execute(new SleepingTask());
        }
        exec.shutdown();
    }
}

对sleep()的调用可以抛出InterruptedException异常,并且你可以看到,它在run()中被捕获。因为异常不能跨线程传播回main(),所以你必须在本地处理所有在任务内部出现的异常

Java SE5引入了更加显式的sleep()版本,作为TimeUnit类的一部分,就像上面示例所示的那样,这个方法允许你指定sleep()延迟的事件单元。因此可以提供更好的可阅读性。

TimeUnit还可以被用来执行转换。

优先级

​ 线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理器现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。然而这并不是意味着优先权较低的线程将得不到执行(也就是说优先级不会造成死锁)。优先级较低的线程仅仅是执行的频率较低。

在绝大部分事件中,所有线程都应该以默认的优先级运行,试图操作优先级通常是一种错误。

下面是一个演示优先级等级的示例,你可以用getPriority()来读取现有线程的优先级,并且在任何时刻都可以通过setPriority()来修改它

package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
public class SimplePriorities implements Runnable{
    private int countDown = 5;
    private volatile double d;
    private int priority;
    public SimplePriorities(int priority){
        this.priority = priority;
    }
    public String toString(){
        return Thread.currentThread() + ": " + countDown;
    }
    public void run(){
        // 在run()中设置优先级
        Thread.currentThread().setPriority(priority);
        while (true){
            for(int i = 1; i < 100000; i++){
                d += (Math.PI + Math.E) / (double)i;
                if(i % 1000 == 0)
                    Thread.yield();
                System.out.println(this);
                if(--countDown == 0)
                    return;
            }
        }
    }
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
        }
        exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
        exec.shutdown();
    }

}

toString()方法被覆盖,以便使用Thread.toString()方法来打印自己的线程名称。

通过调用Thread.currentThread()来获得对驱动该任务的Thread对象的引用

在run()中执行了100000次开销相当大的浮点运算。包括double类型的加法与除法。变量d是volatile的,以努力确保不进行任何编译器优化。如果没有加如这些运算的话。就看不到设置优先级的效果。

不同系统的优先级不同,所以建议只使用

MAX_PRIORITY
NORM_PRIORITY
MIN_PRIORITY

让步 - yield()

如果知道已经完成了在run()方法的循环的一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:你的工作已经做的差不多了,可以让别的线程使用cpu了。

这个暗示通过yield()方法来作出(不过这是一个暗示,没有任何机制保证它会被采纳)。当调用yield()时,你也是在建议具有相同优先级的其他线程可以运行。

LiftOff.java使用yield()在各种不同的LiftOff任务之间产生分布良好的处理机制。尝试着注释掉LiftOff.run()中的Thread.yield()其实没啥区别。

后台(daemon)线程

是指在程序运行的时候在后台提供一种通用服务的线程。并且这种线程并不属于程序中不可或缺的部分。

因此当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中所有后台线程。

反过来说,只要有非后台线程还在运行,程序就不会dead。执行main()就是一个非后台线程

package com.fallsown.fallsownthread;

import java.util.concurrent.TimeUnit;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
public class SimpleDaemons implements Runnable{
    public void run(){
        try{
            while(true){
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread() + " " + this);
            }
        }catch (InterruptedException e){
            System.out.println("sleep() Interrupted");
        }
    }
    public static void main(String[] args) throws Exception{
        for(int i = 0; i < 10; i++){
            Thread daemon = new Thread(new SimpleDaemons());
            // 设置为后台进程
            daemon.setDaemon(true);
            daemon.start();
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(175);
    }
}

必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。

一旦main()完成其工作,就没什么能阻止程序终止了,因为除了后台线程之外,没有线程在运行了。main()线程被设定为短暂睡眠。

SimpleDaemons.java创建了显式的线程,以便可以设置它们的后台标志。通过编写定制的ThreadFactory可以定制由Executor创建的线程的属性(后台、优先级、名称);

package com.fallsown.fallsownthread;

import java.util.concurrent.ThreadFactory;

/**
 * 交给线程工厂去创建线程
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
public class DaemonThreadFactory implements ThreadFactory {
    public Thread newThread(Runnable r){
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

之后就可以使用

package com.fallsown.fallsownthread;

import java.util.concurrent.TimeUnit;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
public class SimpleDaemons implements Runnable{
    public void run(){
        try{
            while(true){
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread() + " " + this);
            }
        }catch (InterruptedException e){
            System.out.println("sleep() Interrupted");
        }
    }
    public static void main(String[] args) throws Exception{
        ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory);
        for(int i = 0; i < 10; i++){
            exec.execute(new DaemonFromFactory())
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(175);
    }
}

但是后台进程由于非后台线程的限制,会导致突然死亡,导致finally语句不会被执行。

加入一个线程 -- join()方法

一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。

如果某个线程在另一个线程t上调用t.join(),此线程将会被挂起,直到目标线程结束才恢复。

也可以在调用join()时带上一个超时参数(单位可以是毫秒或纳秒),这样如果目标线程在这段时间到期还没有结束的话join()方法总能返回

对join方法的调用可以被终端,做法就是在调用线程上调用interrupt()方法,这时需要用到try-catch

package com.fallsown.fallsownthread;

import java.util.StringJoiner;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
class Sleeper extends Thread{
    private int duration;
    public Sleeper(String name,int sleepTime){
        // 创建线程名称
        super(name);
        duration = sleepTime;
        // 运行本线程
        start();
    }
    public void run(){
        try{
            sleep(duration);
        }catch (InterruptedException e){
            System.out.println(getName() + " was interrupted. " + "isInterrupted(): " + isInterrupted());
            return;
        }
        System.out.println(getName() + " has awakened");
    }
}

class Joiner extends Thread{
    private Sleeper sleeper;
    public Joiner(String name,Sleeper sleeper){
        super(name);
        this.sleeper = sleeper;
        // 开始任务
        start();
    }
    public void run(){
        try {
            sleeper.join();
        }catch (InterruptedException e){
            System.out.println("Interrupted");
        }
        System.out.println(getName() + " join completed");
    }
}

public class Joining {
    public static void main(String[] args){
        Sleeper sleepy = new Sleeper("Sleepy",1500),
                grumpy = new Sleeper("Grumpy",1500);
        Joiner
                doper = new Joiner("Dopey",sleepy),
                doc = new Joiner("Doc",grumpy);
        // grumpy被中断
        grumpy.interrupt();
    }
}

Sleeper是一个Thread类型,它要休眠一段时间,这段时间由构造函数来指定。

在run()中,sleep()方法由可能在指定时间期满时返回,但也可能被中断。

在catch()子句中,在异常被捕获的时候,这个标志总为假。除异常之外,这个标志还可用于其他情况,比如线程可能会检查其中断状态。

但是异常捕获的时候会清理这个标志,所以总是false

注意,Java SE5的java.util.concurrent类库包含诸如CyclicBarrier这样的工具,比join()更加适合。

创建有响应的用户界面

使用线程的动机之一就是建立有响应的用户界面。

package com.fallsown.fallsownthread;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
class UnresponsiveUI{
    private volatile double d = 1;
    public UnresponsiveUI() throws Exception{
        while(d > 0){
            d = d + (Math.PI + Math.E) / d;
        }
        System.in.read();   //never gets here
    }
}

public class ResonsiveUI extends Thread{
    private static volatile double d = 1;
    public ResonsiveUI(){
        setDaemon(true);
        start();
    }
    public void run(){
        while(true){
            d = d + (Math.PI + Math.E) / d;
        }
    }

    public static void main(String[] args) throws Exception{
        new ResonsiveUI();
        System.in.read();
        System.out.println(d);
    }
}

UnresponsiveUI在一个无线的while循环里执行运算,显然程序不可能到达读取控制台输入的哪一行(编译器被欺骗了,相信while的条件使得程序能到达读取控制台那一行)

如果把建立UnresponsiveUI的那一行的注释解除掉再运行程序,那么要终止它,就只能杀死这个进程。

要想让程序有相应,就得把计算程序放在run()方法中,这样就能让出处理器给别的程序。

线程组

没啥屌用 java编程思想 1146页

捕获异常

由于线程的本质特征,导致无法从线程中捕获逃逸的异常。一旦异常掏出任务的run()方法,这样就会向外传播到控制台

再Java SE5之前,通过使用线程组来捕获这些异常。

但是在Java SE5之后则可以用Executor来解决这个问题。

如下面任务会抛出一个异常
package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
public class ExceptionThread implements Runnable {
    public void run(){
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}
输出如下
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\softwareHome\IDEA\IntelliJ IDEA 2018.3.5\lib\idea_rt.jar=50808:D:\softwareHome\IDEA\IntelliJ IDEA 2018.3.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\ideaSave\fallsownTest01\out\production\fallsown;E:\chromeDownload\xom-1.3.7.jar" com.fallsown.fallsownthread.ExceptionThread
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
	at com.fallsown.fallsownthread.ExceptionThread.run(ExceptionThread.java:12)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

将main()主体放在try-catch()块中是没有作用的

为了解决这个问题,我们需要修改Executor产生线程的方式。Thread.UncauthtExceptionHandler是Java SE5中的新接口

它允许你在每个Thread对象上都附着一个异常处理器。

Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而面临死亡的时候被调用

为了使用它,我们创建一个新类型ThreadFactory,它将每个创建的Thread对象上附着一个Thread.UncaughtExceptionHandler,我们将这个工厂传递给Executors创建新的ExecutorService的方法

package com.fallsown.fallsownthread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * @author: 红烧鲈鱼
 * @date: 2021/4/14
 */
class ExceptionThread2 implements Runnable{
    public void run(){
        Thread t = Thread.currentThread();
        System.out.println("run() by " + t);
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    public void uncaughtException(Thread t, Throwable e){
        System.out.println("caught " + e);
    }
}

// 继承于线程工厂
class HandlerThreadFactory implements ThreadFactory{
    public Thread newThread(Runnable r){
        System.out.println(this + " creating new Thread");
        Thread t = new Thread(r);
        System.out.println("created " + t);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        return t;
    }
}

public class CaptureUncaughtException {
    public static void main(String[] args) {
        // CachedThreadPool中的参数是使用这个线程工厂构造器
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
    }
}

在程序中设置了额外的跟踪机制,用来验证工厂创建的线程会传递给UncaughtExceptionHandler。

现在可以看到,未捕获异常是通过uncaughtException来捕获的。

public class SettingDefaultHandler{
    Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new ExceptionThread());
}
这篇关于java线程及线程池-使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!