基础信息
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即为工厂模式的一种运用,通过这个可以在生成一个线程时对齐进行必要的操作,免去每个线程单独去设置的麻烦。