Java中线程的常用操作-后台线程、自定义线程工厂ThreadFactpry、join加入一个线程、线程异常捕获

news2024/11/24 15:48:44

场景

Java中Thread类的常用API以及使用示例:

Java中Thread类的常用API以及使用示例_霸道流氓气质的博客-CSDN博客

上面讲了Thread的常用API,下面记录下线程的一些常用操作。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主

实现

后台线程

后台线程,是指运行时在后台提供的一种服务线程,这种线程不是属于必须的。

当所有非后台线程结束时,程序就停止了,同时会终止所有的后台线程。

即只要有任何非后台线程还在运行,程序就不会终止。

实现方式:

            Thread daemon = new Thread(new SimpleDaemons());
            daemon.setDaemon(true);

示例代码:

public class SimpleDaemons implements Runnable{

    @Override
    public void run() {
        while(true){
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread()+":"+this);
            } catch (InterruptedException e) {
                System.out.println("sleep() interrupted");
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        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);
    }
}

说明:

在每次的循环中会创建10个线程,并把每个线程设置为后台线程,然后开始运行,for循环会进行十次,

然后输出信息,随后主线程休眠一段时间后停止运行。在每次run循环中,都会打印当前线程的信息,

主线程运行完毕,程序就执行完毕了。因为daemon是后台线程,无法影响主线程的执行。但是当你把daemon.setDaemon(true)去掉时,

while(true)会进行无限循环,那么主线程一直在执行重要的任务,所以会一直循环下去无法停止。

线程工厂ThreadFactory

按需要创建线程的对象。使用线程工厂替代了Thread或者Runnable接口的硬连接,

使程序能够使用特殊的线程子类,优先级等。ThreadFactory是一个接口,

它只有一个方法就是创建线程的方法。

示例代码一:

上面实现后台线程可以通过线程工厂实现

import java.util.concurrent.ThreadFactory;

public class DaemonThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setDaemon(true);
        return thread;
    }
}

然后新建线程池时传递线程工厂

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

public class DaemonFromFactory implements Runnable{
    @Override
    public void run() {
        while (true){
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread()+":"+this);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool(new DaemonThreadFactory());
        for (int i = 0; i < 10; i++) {
            executorService.execute(new DaemonFromFactory());
        }
        System.out.println("all daemons started");
        TimeUnit.MILLISECONDS.sleep(500);
    }
}

示例代码二:

创建线程池时如果不指定线程工厂会使用默认的线程工厂,查看ThreadPoolExecutor的构造方法源码

 

Executors.defaultThreadFactory()的源码实现

 

假如我们要自定义线程池的线程名称前缀,可以参考DefaultThreadFactory自定义线程工厂来实现,方便

出现异常时能快速定位。

public class MyThreadFactory implements ThreadFactory {

    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    public MyThreadFactory(String threadName) {
        SecurityManager s = System.getSecurityManager();
        group = (s !=null)?s.getThreadGroup():Thread.currentThread().getThreadGroup();
        if(threadName == null || threadName.isEmpty()){
            threadName = "pool";
        }
        namePrefix = threadName + poolNumber.getAndIncrement()+"-thread-";
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group,r,namePrefix+threadNumber.getAndIncrement(),0);
        if(t.isDaemon()){
            t.setDaemon(false);
        }
        if(t.getPriority()!= Thread.NORM_PRIORITY){
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}

然后在新建线程池时

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadFactoryDemo {
    public static final ThreadPoolExecutor pool = new ThreadPoolExecutor(5,10,5, TimeUnit.SECONDS,new ArrayBlockingQueue<>(100),new MyThreadFactory("badao"));

    public static void main(String[] args) {
        pool.execute(()->{
            Integer integer = getOrderInfo();
        });
    }
    private static Integer getOrderInfo(){
        try {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                list.add(i);
            }
            return list.get(11);
        }catch (Exception exception){
            throw new IndexOutOfBoundsException("数组越界");
        }
    }
}

这样在出现数组越界时,就会以指定前缀提示

 

join()方法加入一个线程

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

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

可以用t.isAlive返回为真假判断。也可以在调用join时带上一个超时参数,来设置到期时间,时间到期,join方法自动返回

对join的调用也可以被中断,做法是在线程上调用interrupted方法,这时需要用到try...catch子句。

示例代码:

public class TestJoinMethod  extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                TimeUnit.MICROSECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread() +" "+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoinMethod joinMethod = new TestJoinMethod();
        TestJoinMethod joinMethod1 = new TestJoinMethod();
        TestJoinMethod joinMethod2 = new TestJoinMethod();
        joinMethod.start();
        joinMethod.join();
        joinMethod1.start();
        joinMethod2.start();
    }
}

上面加了join的运行结果:

    //Thread[Thread-0,5,main] 0
    //Thread[Thread-0,5,main] 1
    //Thread[Thread-0,5,main] 2
    //Thread[Thread-0,5,main] 3
    //Thread[Thread-0,5,main] 4
    //Thread[Thread-1,5,main] 0
    //Thread[Thread-2,5,main] 0
    //Thread[Thread-2,5,main] 1
    //Thread[Thread-1,5,main] 1
    //Thread[Thread-2,5,main] 2
    //Thread[Thread-1,5,main] 2
    //Thread[Thread-1,5,main] 3
    //Thread[Thread-2,5,main] 3
    //Thread[Thread-1,5,main] 4
    //Thread[Thread-2,5,main] 4

如果将join去掉后的运行结果:

    //Thread[Thread-0,5,main] 0
    //Thread[Thread-1,5,main] 0
    //Thread[Thread-2,5,main] 0
    //Thread[Thread-0,5,main] 1
    //Thread[Thread-1,5,main] 1
    //Thread[Thread-2,5,main] 1
    //Thread[Thread-0,5,main] 2
    //Thread[Thread-2,5,main] 2
    //Thread[Thread-1,5,main] 2
    //Thread[Thread-0,5,main] 3
    //Thread[Thread-1,5,main] 3
    //Thread[Thread-2,5,main] 3
    //Thread[Thread-0,5,main] 4
    //Thread[Thread-1,5,main] 4
    //Thread[Thread-2,5,main] 4

可以看到joinMethod.start();当中的所有的内容都执行完后,才轮到后面的joinMethod1.start();和joinMethod2.start();执行。

换句话说,它会导致当前运行的线程停止运行,直到它加入的线程完成其任务。

线程异常捕获

由于线程的本质,使你不能捕获从线程中逃逸的异常,一旦异常逃出任务的run方法,它就会向外传播到控制台,除非你采用特殊的步骤

捕获这种错误的异常。下面的任务会在run方法的执行期间抛出一个异常,并且这个异常会抛出到run方法的外面,

而且main方法无法对它进行捕获。

public class ExceptionThread implements Runnable{
    @Override
    public void run() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        try {
            ExecutorService executorService = Executors.newCachedThreadPool();
            executorService.execute(new ExceptionThread());
        }catch (Exception exception){
            System.out.println(exception);
        }
    }
}

为了解决这个问题,我们需要修改Executor产生线程的方式,java5提供了一个新的接口Thread.UncaughtExceptionHandler,

它允许你在每个Thread上都附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因

未捕获临近死亡时被调用

下面模拟抛出异常

public class ExceptionThread2 implements Runnable{
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run() by" + t);
        System.out.println("eh = "+t.getUncaughtExceptionHandler());
        //手动抛出异常
        //throw new RuntimeException();
        int a = 1/0;
    }
}

然后实现Thread.UncaughtExceptionHandler接口,创建异常处理器

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

自定义线程工厂

import java.util.concurrent.ThreadFactory;

public class HandlerThreadFactory implements ThreadFactory {
    @Override
    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("ex = "+t.getUncaughtExceptionHandler());
        return t;
    }
}

新建线程池并调用

public class CaptureUncaughtException {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool(new HandlerThreadFactory());
        executorService.execute(new ExceptionThread2());
    }
}

运行结果

 

在程序中添加了额外的追踪机制,用来验证工厂创建的线程会传递给UncaughtExceptionHandler,

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/425298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Doris(4):建表

可以通过在mysql-client中执行以下 help 命令获得更多帮助&#xff1a; help create table 1 基本概念 在 Doris 中&#xff0c;数据都以表&#xff08;Table&#xff09;的形式进行逻辑上的描述。 1.1 Row & Column 一张表包括行&#xff08;Row&#xff09;和列&#…

从零开始:如何集成美颜SDK到你的应用中

现在&#xff0c;随着人们对于美的追求不断提升&#xff0c;美颜应用已经成为了人们生活中不可或缺的一部分。在应用中&#xff0c;美颜功能的实现离不开美颜SDK的支持。那么&#xff0c;如何集成美颜SDK到你的应用中呢&#xff1f;下面&#xff0c;我们就来一步步了解。 第一…

Linux复习 / 线程相关----线程互斥 QA梳理

文章目录前言线程互斥Q&#xff1a;什么是临界资源&#xff1f;临界区呢&#xff1f;Q&#xff1a;什么是互斥&#xff1f;Q&#xff1a;数据不一致的本质是什么&#xff1f;Q&#xff1a;用锁对共享资源进行保护的前提是&#xff1a;锁也要作为共享资源被其他线程使用。那么用…

独家 | 招商银行:玩转校园招聘新方式 挖掘金融科技新人才

数字经济时代&#xff0c;金融科技人才队伍的引进与培养是招商银行人才体系建设的关键任务。 01.金融科技校招2大核心课题 招商银行数字化转型过程中&#xff0c;线上化、生态化、平台化、智能化、数据化全面加速发展&#xff0c;对人才队伍能力提出新要求。 2大核心课题&am…

Git的一些使用

虽然说这也不是啥重要的内容&#xff0c;但是作为计算机人也得学学&#xff0c;了解了解。 一些预备内容 首先得下载git&#xff0c;这个就不多说了。 安装完了之后&#xff0c;首先要做的就是设置用户名称和邮箱地址&#xff0c;因为每次Git提交都会使用该信息&#xff0c;…

I.MX6ULL_Linux_驱动篇(33) pinctrl与gpio子系统

上一章我们编写了基于设备树的 LED 驱动&#xff0c;但是驱动的本质还是没变&#xff0c;都是配置 LED 灯所使用的 GPIO 寄存器&#xff0c;驱动开发方式和裸机基本没啥区别。 Linux 是一个庞大而完善的系统&#xff0c;尤其是驱动框架&#xff0c;像 GPIO 这种最基本的驱动不可…

Linux实战学习

文章目录一、Linux权限信息权限控制信息chmodifconfigpingnmap netstatps killzip unzip常用快捷键二、搭建Java环境yumJDKTomcatMysql三、部署Web项目到服务器一、Linux权限信息 Linux中&#xff0c;拥有最大权限的账户为: root(超级管理员)&#xff0c;而普通用户在很多地方…

UWB成为智慧工厂时代的代表技术

UWB成为智慧工厂时代的代表技术 随着智慧工厂的到来&#xff0c;在人员安全问题较为重要的行业中&#xff0c;为了避免人员安全事故的出现&#xff0c;各家企业都逐步装备了UWB定位系统。UWB信号的辐射非常低&#xff0c;通常只有手机辐射的千分之一&#xff0c;因此在工业上应…

【 Spring MVC 核心功能(二) - 获取参数(上)】

文章目录一、获取单个参数二、获取多个参数三、获取对象四、后端参数重命名4.1 使用 RequestParam 重命名参数4.2 RequestParam 中参数必传4.3 设置非必传参数五、使用 PathVariable 获取URL中参数一、获取单个参数 在 Spring MVC 中可以直接⽤⽅法中的参数来实现传单个参&…

uni-app:登录与支付-- 三秒后自动跳转

三秒后自动跳转 三秒后自动跳转到登录页面 需求描述&#xff1a;在购物车页面&#xff0c;当用户点击 “结算” 按钮时&#xff0c;如果用户没有登录&#xff0c;则 3 秒后自动跳转到登录页面 在 my-settle 组件的 methods 节点中&#xff0c;声明一个叫做 showTips 的方法&am…

Vue:生命周期

1、定义 生命周期函数&#xff08;俗称&#xff1a;钩子函数&#xff09; 根据vue整个渲染机制&#xff0c;在渲染的每个关键点上&#xff0c;提供对应的函数&#xff0c;进行一些相关的业务操作。 2、四个阶段 初始阶段&#xff1a;beforeCreate()&#xff1a;可以加loadi…

vue-qr 生成二维码-使用

1、vue-qr官网说明 vue-qr - npm 2、使用 2.1 安装 vue-qr npm install vue-qr --save 2.2 代码 import vueQr from vue-qr; <el-dialog title"摘要" :visible.sync"openSummary" width"700px" append-to-body> <el-row> <el…

Oracle基础(表空间、用户、授权、表、数据类型、数据导入导出等)

Oracle基础(表空间、用户、授权、表、数据类型、数据导入导出等1 创建表空间1.1 概述1.2 语法&#xff1a;1.3 示例&#xff1a;2 创建用户2.1 语法2.2 示例2.3 用户授权类型3 表的创建、修改、删除3.1 表创建3.1.1 概述3.1.2 语法3.1.3 示例3.1.4 表的数据类型3.2 表修改3.2.1…

Oracle系列之七:表的创建与管理

Oracle表的创建与管理1. 表的创建2. 表的修改3. 表中数据的增删改查4. 表的Merge5. 表的删除6. 表的重命名7. 表的索引8. 表的约束9. dual表表是Oracle数据库中最基础的存储对象&#xff0c;用于存储数据。本文主要介绍了Oracle表的创建与管理&#xff0c;包括表的创建、修改、…

图像处理:双边滤波算法

今天主要是回顾一下双边滤波&#xff0c;我曾经在这篇——图像处理&#xff1a;推导五种滤波算法中推导过它&#xff0c;其中包含了我自己写的草稿图。 目录 双边滤波算法原理 &#xff08;1&#xff09;空间域核 &#xff08;2&#xff09;值域核 理解双边滤波 空域权重​…

Reactor模型在库存指令模块中的运用

Reactor是一种高性能网络模型&#xff0c;在netty、redis、nginx、kafaka、memcached等重要组件&#xff0c;以及唯品会自研的OSP框架都有应用&#xff0c;Reactor模型在提升性能和解耦方面都做得非常好&#xff0c;其编程思想也可以运用到业务系统的开发当中&#xff0c;本文主…

VUE:常见的面试题和答案

1. Vue组件的生命周期有哪些&#xff0c;它们的执行顺序是什么? 答&#xff1a;Vue组件的生命周期包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed等。它们的执行顺序如下&#xff1a; beforeCreate -> created ->…

21从零开始学Java之while与do-while循环的用法有什么不同?

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在上一篇文章中&#xff0c;壹哥给大家讲解了循环的概念&#xff0c;并重点给大家讲解了for循环的使用…

腾讯轻联流程运行错误如何排查问题?

我们在使用腾讯轻联时&#xff0c;会发现有些流程并没有运行成功&#xff0c;例如我们希望数据可以及时同步到腾讯文档&#xff0c;流程也有执行&#xff0c;但是却执行失败了&#xff0c;那么如何排查问题呢&#xff1f; 其中有几类常见问题 1. 流程不运行的问题请查看&…

【WCH】基于STM32F1标准库程序烧录到CH32F203中运行方法

【WCH】基于STM32标准库程序烧录到CH32F203中运行方法&#x1f4cc;相关篇《关于CH32F203程序下载方式说明》 ✨看到CH32F203手册上写的该芯片也是ARM Cortex-M3内核&#xff0c;那么上层代码应该也是兼容的&#xff0c;为例证实这一点&#xff0c;开干&#xff0c;先来一个简单…