Java中线程的常用操作-后台线程、自定义线程工厂ThreadFactpry、join加入一个线程、线程异常捕获
场景
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{@Overridepublic 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 {@Overridepublic 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{@Overridepublic 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-";}@Overridepublic 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{@Overridepublic 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{@Overridepublic 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{@Overridepublic 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{@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("caught "+ e);}
}
自定义线程工厂
import java.util.concurrent.ThreadFactory;public class HandlerThreadFactory implements ThreadFactory {@Overridepublic 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来捕获的。