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

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

时间:2023-04-17 13:34:29浏览次数:42  
标签:Java 自定义 Thread System 线程 new main public

场景

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

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126596884

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

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

后台线程

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

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

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

实现方式:

            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来捕获的。

标签:Java,自定义,Thread,System,线程,new,main,public
From: https://www.cnblogs.com/badaoliumangqizhi/p/17325551.html

相关文章

  • 【迭代器设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    简介迭代器模式(IteratorPattern),是一种结构型设计模式。给数据对象构建一套按顺序访问集合对象元素的方式,而不需要知道数据对象的底层表示。迭代器模式是与集合共存的,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像Java中的Collection,List、Set、Map等,这些集合都有自......
  • java json 四个格式
    java官方<dependency><groupId>org.json</groupId><artifactId>json</artifactId><version>20220320</version></dependency>codeJSONObjectjsonObject=newJSONObject();jsonObject.put("secretKey......
  • idea Java json 复制字符串会出现空格的问题
    普通JSON{"secretKey":"2513e9c533c14271a1bc8a52eacecebe","appKey":"19b9257a1f464e93b087af9d12572ce1"}复制idea{\"secretKey\":\"2513e9c533c14271a1bc8a52eacecebe\",\"appKey\":\&......
  • java环境变量
    jdk安装路径C:\ProgramFiles\Java\jdk1.6.0_21java_homeC:\ProgramFiles\Java\jdk1.6.0_21classpath.;%JAVA_HOME%lib;%JAVA_HOME%lib\tools.jarPath%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin检查是否安装成功java-version  ant环境的配置配置ant 和 java的环境  ......
  • Java位运算符
    前置知识原码、反码、补码-原码:第一位表示符号,其余位表示值。如2原码:00000010;-2原码:10000010-反码:正数的反码是原码本身,负数的反码在原码基础上,符号位不变,其他位取反。如:2反码:00000010;-2反码:11111101-补码:正数的反码是原码本身,负数的补码在原码基础上,符号位不变,其他......
  • 六大JavaScript使用小技巧
    之前写过一些关于编程的小技巧,今天已久写一些JS中用到的小技巧,非常实用,如果熟悉了实用起来相当快捷方便。1.类型强制转换1.1string强制转换为数字可以用*1来转化为数字(实际上是调用.valueOf方法)然后使用Number.isNaN来判断是否为NaN,或者使用a!==a来判断是否为NaN,因为......
  • 线程池分批处理excel数据
    一、场景在开发excel处理数据时,因为数据库的卡顿,一次插入2000条数据速度可能需要1min左右,所以考虑使用线程池;每200个分组,有n组,就开(n+1)个线程去分批同时处理这些数据。二、依赖1.pom.xml<!--工具类hutool,java兵器库,用于读取excel--><dependency>......
  • (之前的项目复习)我的Java项目实战--校园餐饮商户外卖系统07(优化)
    开发笔记七缓存优化问题说明用户数量多,系统访问量大频繁访问数据库,系统性能下降,用户体验差环境搭建maven坐标在项目的pom.xm1文件中导入springdataredis的maven坐标:点击查看代码<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s......
  • Java Web应用设计中验证码的生成和应用方法
    在JavaWeb应用设计中验证码的设计是一个必不可少的环节,由于验证码技术具有随机性较强、简单的特点,能够在一定程度上阻止网络上的恶意访问,在互联网领域得到了广泛的应用,如防止破解密码、刷票、论坛灌水、刷页、注册等恶意操作。百度上对验证码的定义是:(CAPTCHA)“CompletelyAut......
  • Win32API之实现远程线程注入(九)
    什么是注入注入是一种在不知情或未经许可的情况下向其他进程中注入模块并试图执行它们的技术常见的注入方式有:远程线程注入、APC注入、消息钩子注入、注册表注入、导入表注入、输入法注入等等什么是远程线程注入远程线程注入是一种技术,可以将一个动态链接库(DLL)注入到另一个进......