基础信息
Java语言中有两种方式来创建一个线程:通过继承Thread类,或是实现Runnable接口,并实现其run()
方法,通过得到的Thread实例调用start()
方法,来启动一个线程。处理器将按照自己的策略来分配每个线程的执行,因此线程的执行顺序、时间等是不确定的,如果一个任务依赖于有序的执行,需要自行进行同步之类的操作。另外在创建了一个Thread后,在它的任务执行完毕之前,它将一直存在且垃圾回收器无法回收它。
线程的创建和销毁需要较大的开销,同时当显示的创建一个线程并启动它时,可能会由于代码中的缺陷,导致系统创建了大量的线程导致系统崩溃,在这里可以使用Executor
来启动线程。Executor
可以接收一个Thread或Runnable对象并启动它们,Executor常用的实现类型有CachedThreadPool
, FiexdThreadPool
,SingleThreadExecutor
等,其中 FiexdThreadPool 线程池将创建一个固定大小的线程池,SingleThreadPool则创建单个线程的线程池;每一种类型的线程池都将在可能的情况下复用线程。
线程是没有返回值的,当一个线程启动过后,它将独立于启动它的线程;线程中的任何异常也无法被父线程捕捉到。因此一个线程启动后,要确保它能自行完成任务并处理期间发生的异常。如果需要执行后得到返回值,可以使用Callable
来代替Runnable
,实现其中的call()方法,并且,必须使用ExecutorService.submit()
来调用它。使用实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package io.lovs.java.thread;
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*;
public class TaskLearn { public static void main(String[] args){ ExecutorService es = Executors.newCachedThreadPool(); List<Future<String>> futures = new ArrayList<>(); for(int i=0; i<10; i++){ futures.add(es.submit(new TaskHasResult(i))); }
for(Future<String> f: futures){ try { System.out.println(f.get());
} catch (InterruptedException e) { e.printStackTrace(); return; } catch (ExecutionException e) { e.printStackTrace(); } } } }
class TaskHasResult implements Callable<String> {
private int index; public TaskHasResult(int index){ this.index = index; }
@Override public String call() throws Exception {
return "Task index is " + index; } }
|
submit()方法调用后将返回一个Future对象,通过调用它的get()方法可以得到call()中的返回值。调用get()的时候,方法将被阻塞,直到得到结果为止,可以通过调用isDone()来判断是否已经执行完毕。
线程中的异常
Java中的线程无法向外部抛出异常,简单来说,线程内的一切异常都不能在线程外部catch到,因为Java中的线程的理念是:它们是一个完整独立的个体,能够自行处理完它的任务,包括其中的异常。因此在线程中,异常需要在内部处理,否则将会把错误传播到控制台,并可能终止线程。Java SE5后,为这个问题新增了解决办法:Thread.UncaughtExceptionHandler
接口,它将每个Thread对象都附带一个异常处理器,其中的uncaughtException()
方法将会在线程因未捕获的异常而要终止时被调用。
下面的代码展示了它的简单用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| package io.lovs.java.thread;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory;
public class ThreadCatchException {
public static void main(String[] args){ ExecutorService es = Executors.newCachedThreadPool(new ExceptionThreadFactory()); es.execute(new ExceptionThread());
Thread t2 = new Thread(new ExceptionThread()); t2.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); t2.start(); Thread t3 = new ExceptionThreadFactory().newThread(new ExceptionThread()); t3.start(); }
}
class ExceptionThread implements Runnable{
@Override public void run() { throw new RuntimeException(); } }
class ExceptionThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); return t; } }
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override public void uncaughtException(Thread t, Throwable e) { System.out.println("caught exception"); e.printStackTrace(); } }
|
上面显示了几种方式来使用异常处理器,当直接创建线程并启动时,可以在线程对象上设置该异常处理器。如果是通过线程池来执行线程,可以创建一个自己的ThreadFactory
,它的作用就是为每一个新创建的线程附上一个异常处理器。
ThreadFactory即为工厂模式的一种运用,通过这个可以在生成一个线程时对齐进行必要的操作,免去每个线程单独去设置的麻烦。