Java并发编程从入门到精通 - 第2章:认识Thread

Posted kehuaihan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java并发编程从入门到精通 - 第2章:认识Thread相关的知识,希望对你有一定的参考价值。

线程实现的三种方法:
1、三种实现方式的简记:
 继承Thread类,重写run()方法;
 实现Runnable接口,重写run()方法,子类创建对象并作为Thread类的构造器参数;
 实现Callable接口,重写call()方法,子类创建对象并作为FutureTask类的构造器参数,FutureTask类创建对象并作为Thread类的构造器参数;
2、三种实现方法的比较:
 继承Thread类:因为是单继承,所以扩展性不好;
 实现Runnable接口:接口可以多重实现;并且还可以再继承一个类;扩展性好
 实现Callable接口:Runnable无法返回结果,且不能抛出返回的异常;而Callable接口可以,Callable产生结果,FutureTask可以拿到结果,也可以捕获Callable抛出的异常;
 最常用的就是第二种,实现Runnable接口;

技术分享图片
 1 /**
 2  * 实现线程的方法1:继承Thread类
 3  */
 4 package thread01;
 5 
 6 public class ThreadTest01
 7 {
 8     public static void main(String[] args)
 9     {
10         ThreadDemo01 threadDemo01 = new ThreadDemo01();
11         threadDemo01.start();
12         
13         // 使用下面方法也能达到效果
14         /*Thread thread = new Thread(threadDemo01);
15         thread.start();*/
16         
17         System.out.println("这是主线程:main结束");
18     }
19 }
20 
21 class ThreadDemo01 extends Thread
22 {
23     @Override
24     public void run()
25     {
26         // 子类继承一个父类的时候,不是必须要重写父类的方法;但一个类实现一个接口的时候,这个类必须重写接口的方法;
27         // 默认调用父类的run()方法,为什么要默认调用,因为你重写的时候,可能只是为了调用父类的run()方法;既然自己真的要重写,去掉即可;
28         // super.run();
29         
30         try
31         {
32             // 必须加 try...catch...,即只能捕获,不能抛出异常
33             Thread.sleep(1000);  // 使用sleep()方法模拟做了一些事(具体业务逻辑)
34         } 
35         catch (InterruptedException e)
36         {
37             e.printStackTrace();
38         }
39         
40         System.out.println("这是线程ThreadDemo01");
41     }
42 }
实现线程的方法1:继承Thread类
技术分享图片
 1 /**
 2  * 实现线程的方法2:实现Runnable接口
 3  */
 4 package thread01;
 5 
 6 public class RunnableTest01
 7 {
 8     public static void main(String[] args)
 9     {
10         ThreadDemo02 threadDemo02 = new ThreadDemo02();
11         Thread thread = new Thread(threadDemo02);
12         thread.start();
13         
14         System.out.println("这是主线程:main结束");
15     }
16 }
17 
18 class ThreadDemo02 implements Runnable
19 {
20     @Override
21     public void run()
22     {
23         try
24         {
25             Thread.sleep(1000);
26         } 
27         catch (InterruptedException e)
28         {
29             e.printStackTrace();
30         }
31         
32         System.out.println("这是线程ThreadDemo02");
33     }
34     
35 }
实现线程的方法2:实现Runnable接口
技术分享图片
 1 /**
 2  * 实现线程的方法3:实现Callable接口
 3  * Callable和Future:一个可以产生结果,一个可以拿到结果
 4  */
 5 package thread01;
 6 
 7 import java.util.concurrent.Callable;
 8 import java.util.concurrent.ExecutionException;
 9 import java.util.concurrent.FutureTask;
10 
11 public class CallableTest01
12 {
13     public static void main(String[] args)
14     {
15         ThreadDemo03 threadDemo03 = new ThreadDemo03();
16         FutureTask<String> futureTask = new FutureTask<String>(threadDemo03);
17         Thread thread = new Thread(futureTask);
18         thread.start();
19         
20         try
21         {
22             // 这里既可以捕获也可以抛出异常
23             // 拿到Callable产生的结果
24             System.out.println("获取线程返回的结果:" + futureTask.get());
25         } 
26         catch (InterruptedException | ExecutionException e)
27         {
28             e.printStackTrace();
29         }
30         
31         System.out.println("这是主线程:main结束");
32     }
33     
34     
35     
36 }
37 
38 class ThreadDemo03 implements Callable<String>
39 {
40 
41     @Override
42     public String call() throws Exception
43     {
44         try
45         {
46             // 这里不是必须要捕获异常,因为call方法默认抛出了异常
47             Thread.sleep(1000);
48         } 
49         catch (Exception e)
50         {
51             e.printStackTrace();
52         }
53         
54         // 上面futureTask不调用get()方法,call()方法也会执行(下面这句话也会执行),但肯定拿不到返回结果了
55         System.out.println("这是线程ThreadDemo03");
56         
57         return "线程ThreadDemo03执行完毕返回的结果";
58     }
59     
60 }
实现线程的方法3:实现Callable接口
技术分享图片
 1 /**
 2  * Callable接口的使用:抛出异常
 3  */
 4 package thread01;
 5 
 6 import java.util.concurrent.Callable;
 7 import java.util.concurrent.ExecutionException;
 8 import java.util.concurrent.FutureTask;
 9 
10 public class CallableTest02
11 {
12     public static void main(String[] args)
13     {
14         ThreadDemo04 threadDemo04 = new ThreadDemo04();
15         FutureTask<String> futureTask = new FutureTask<String>(threadDemo04);
16         Thread thread = new Thread(futureTask);
17         thread.start();
18         
19         try
20         {
21             // 异常的捕获和抛出都是通过get()方法体现的,如果没有调用get()方法,main方法执行的时候不会抛出异常;
22             System.out.println("获得线程返回的结果:" + futureTask.get());
23         } 
24         catch (InterruptedException | ExecutionException e)
25         {
26             System.out.println("线程抛出了异常!");
27             e.printStackTrace();
28         }
29         
30         // 如果没有上面的try...catch...语句块,结果只会输出下面一句话,不会出现异常
31         System.out.println("这是主线程:main结束");
32     }
33 }
34 
35 class ThreadDemo04 implements Callable<String>
36 {
37     @Override
38     public String call() throws Exception
39     {
40         int sum = 1 / 0;
41         System.out.println("sum = " + sum);
42         
43         return "线程ThreadDemo04执行完毕返回的结果";
44     }
45     
46 }
Callable接口的使用:抛出异常

Thread里面的属性和方法:

技术分享图片
 1 /**
 2  * Thread类里面的属性和方法
 3  */
 4 package thread01;
 5 
 6 public class ThreadTest02
 7 {
 8     public static void main(String[] args)
 9     {
10         ThreadDemo05 threadDemo05 = new ThreadDemo05();
11         Thread thread = new Thread(threadDemo05);
12         thread.start();
13         
14         // 返回当前正在执行的线程对象的引用
15         Thread currentThread = Thread.currentThread();
16         System.out.println("主线程的名称:" + currentThread.getName());
17         System.out.println("主线程所在的线程组中活动线程的数量:" + Thread.activeCount());
18         System.out.println("主线程的标识符:" + currentThread.getId());
19         System.out.println("主线程的优先级:" + currentThread.getPriority());
20         System.out.println("主线程的状态:" + currentThread.getState());
21         System.out.println("主线程所属的线程组:" + currentThread.getThreadGroup());
22         System.out.println("测试主线程是否处于活动状态:" + currentThread.isAlive());
23         System.out.println("测试主线程是否为守护线程:" + currentThread.isDaemon());
24     }
25 }
26 
27 class ThreadDemo05 implements Runnable
28 {
29     @Override
30     public void run()
31     {
32         try
33         {
34             Thread.sleep(3000);
35         } 
36         catch (InterruptedException e)
37         {
38             e.printStackTrace();
39         }
40         
41         System.out.println("=================================");
42         
43         // 返回当前正在执行的线程对象的引用
44         Thread currentThread = Thread.currentThread();
45         System.out.println("当前线程的名称:" + currentThread.getName());
46         System.out.println("返回当前线程所在的线程组中活动线程的数量:" + Thread.activeCount());
47         System.out.println("返回当前线程的标识符:" + currentThread.getId());
48         System.out.println("返回当前线程的优先级:" + currentThread.getPriority());
49         System.out.println("返回当前线程的状态:" + currentThread.getState());
50         System.out.println("返回当前线程所属的线程组:" + currentThread.getThreadGroup());
51         System.out.println("测试线程是否处于活动状态:" + currentThread.isAlive());
52         System.out.println("测试当前线程是否为守护线程:" + currentThread.isDaemon());
53     }
54     
55 }
Thread类里面的属性和方法

线程的中断机制:
1、详述:
 Java中断机制是一种协作机制,也就是说通过中断不能直接终止另一个线程,而需要被请求中断的线程自己处理中断,且该线程可以选择接受请求中断自己,也可以选择不接受请求不中断自己;
 每个线程都有一个boolean类型的标识(中断状态位),代表是否有中断请求(该请求可以来自所有线程,包括被中断的线程本身),如果有中断请求,该标志位会被设置为true;
2、三个方法的比较:
 public void interrupt():
  用于中断线程;调用该方法仅仅只是将线程的中断状态位设为true,并不会真的停止线程,还是需要用户自己去监视线程的状态位并做处理;
  中断是通过调用Thread.interrupt()方法来做的;这个方法通过修改被调用线程的中断状态来告知那个线程,说它被请求中断了;对于非阻塞中的线程,只是改变了中断状态,即Thread.isInterrupted()将返回true;对于可取消的(不解)阻塞状态中的线程,比如等待在这些函数上的线程:Thread.sleep(),Object.wait(),Thread.join()等,这个线程收到中断信号后,会抛出InterruptedException,同时会把中断状态置回为false;
  当一个线程处于中断状态时(意思是它的中断状态位为true),如果再由wait、sleep以及jion三个方法引起的阻塞,那么JVM会将线程的中断标志重新设置为false,并抛出一个InterruptedException异常;
  本质作用:根据try-catch功能块捕捉jvm抛出的InterruptedException异常来做各种处理,比如如何退出线程;总之interrupt的作用就是需要用户自己去监视线程的状态位并做处理;
 public static boolean interrupted():返回线程的上次的中断状态,并清除中断状态(清除是什么意思,将true改为false,还是既不是true也不是false);
 public boolean isInterrupted():判断线程是否中断;

技术分享图片
 1 /**
 2  * 线程的中断机制1
 3  */
 4 package thread01;
 5 
 6 public class InterruptTest01
 7 {
 8     public static void main(String[] args) throws InterruptedException
 9     {
10         ThreadDemo06 threadDemo06 = new ThreadDemo06();
11         Thread thread = new Thread(threadDemo06);
12         System.out.println("启动线程...");
13         thread.start();
14         
15         Thread.sleep(1000);
16         
17         System.out.println("请求中断线程...");
18         // 这里的thread就是指子线程,但是是在主线程中执行这条语句的,所以叫主线程请求中断子线程
19         thread.interrupt();
20         System.out.println("判断被请求中断的线程是否被中断(判断中断标志位):" + thread.isInterrupted());
21         
22         Thread.sleep(1000);
23         
24         // 请求中断主线程(线程请求中断自己)
25         Thread.currentThread().interrupt();
26         // 判断主线程是否被请求中断
27         System.out.println("判断主线程main是都被中断(判断中断标志位):" + Thread.currentThread().isInterrupted());
28         
29         System.out.println("应用程序执行完毕,退出...");
30         
31     }
32 }
33 
34 class ThreadDemo06 implements Runnable
35 {
36     boolean flag = false;
37     
38     @Override
39     public void run()
40     {
41         while(!flag)
42         {
43             System.out.println("线程开始执行...");
44             
45             long currentTime = System.currentTimeMillis();
46             while(System.currentTimeMillis() - currentTime < 5000)
47             {
48                 // 让该循环持续一段时间,让上面打印语句执行次数少一点
49             }
50             
51             System.out.println("判断当前线程是否被中断(判断中断标志位):" + Thread.currentThread().isInterrupted());
52         }
53         
54         System.out.println("线程在中断请求下中断退出");
55     }
56 }
57 
58 /*
59 运行结果:
60 启动线程...
61 线程开始执行...
62 请求中断线程...
63 判断被请求中断的线程是否中断(判断中断标志位):true
64 应用程序执行完毕,退出...
65 线程开始执行...
66 线程开始执行...
67 线程开始执行...
68 线程开始执行...
69 线程开始执行...
70 线程开始执行...
71 
72 根据结果可以看出,主线程请求中断正在执行的子线程,只是将子线程的中断标志位改变了,并没有真正中断子线程;
73 子线程根据自己的需要判断是否接受中断请求,可以接受中断,也可以接受不中断;
74 本实例中子线程并没有被中断;
75 */
线程的中断机制1
技术分享图片
 1 /**
 2  * 线程的中断机制2
 3  */
 4 package thread01;
 5 
 6 public class InterruptTest02
 7 {
 8     public static void main(String[] args) throws InterruptedException
 9     {
10         ThreadDemo07 threadDemo07 = new ThreadDemo07();
11         Thread thread = new Thread(threadDemo07);
12         System.out.println("启动线程...");
13         thread.start();
14         
15         Thread.sleep(5000);
16         
17         System.out.println("请求中断线程...");
18         thread.interrupt();
19         
20         Thread.sleep(1000);
21         System.out.println("判断被请求中断的线程是否被中断(仅判断中断标志位):" + thread.isInterrupted());
22         
23         System.out.println("应用程序退出!");
24     }
25 }
26 
27 class ThreadDemo07 implements Runnable
28 {
29     private boolean flag = false;
30     
31     @Override
32     public void run()
33     {
34         while(!flag)
35         {
36             System.out.println("线程开始执行...");
37             
38             long currentTime = System.currentTimeMillis();
39             while(System.currentTimeMillis() - currentTime < 1000)
40             {
41                 
42             }
43             
44             // 需要线程自己处理中断请求
45             if(Thread.currentThread().isInterrupted())
46             {
47                 break;
48             }
49         }
50         
51         System.out.println("线程在中断请求下中断退出!");
52     }
53 }
54 
55 /*
56 运行结果:
57 启动线程...
58 线程开始执行...
59 线程开始执行...
60 线程开始执行...
61 线程开始执行...
62 线程开始执行...
63 线程开始执行...
64 请求中断线程...
65 线程在中断请求下中断退出!
66 判断被请求中断的线程是否被中断(仅判断中断标志位):true
67 应用程序退出!
68 
69 根据结果可以看出:被请求中断的线程需要自己根据需要决定是否中断退出;
70 */
线程的中断机制2
技术分享图片
 1 /**
 2  * 线程的中断机制3
 3  */
 4 package thread01;
 5 
 6 public class InterruptTest03
 7 {
 8     public static void main(String[] args) throws InterruptedException
 9     {
10         ThreadDemo08 threadDemo08 = new ThreadDemo08();
11         Thread thread = new Thread(threadDemo08);
12         System.out.println("启动线程...");
13         thread.start();
14         
15         Thread.sleep(2000);
16         System.out.println("2秒后请求中断线程(线程还在休眠)...");
17         thread.interrupt();
18         
19         Thread.sleep(1000);
20         System.out.println("应用程序结束退出!");
21         
22     }
23 }
24 
25 class ThreadDemo08 implements Runnable
26 {
27     @Override
28     public void run()
29     {
30         try
31         {
32             System.out.println("线程正在休眠5秒钟...");
33             Thread.sleep(5000);
34             // Thread.currentThread().join(5000);
35         } 
36         catch (InterruptedException e)
37         {
38             System.out.println("线程休眠被打断,抛出异常...");
39         }
40     }
41 }
42 /*
43 运行结果:
44 启动线程...
45 线程正在休眠5秒钟...
46 2秒后请求中断线程(线程还在休眠)...
47 线程休眠被打断,抛出异常...
48 应用程序结束退出!
49 
50 根据结果可以看出:处于睡眠中(调用sleep()方法)的线程,如果被请求中断,就会抛出中断异常(InterruptedException)
51 */
线程的中断机制3

线程的生命周期:
1、线程生命周期的5中状态:
(1)、新建(new Thread):此时线程有自己的内存空间,但并没有运行;且线程还不是活着的;
(2)、就绪(runnable):线程已经被启动(具备了运行条件),正在等待被分配给CPU时间片(不一定会被立即执行,处于线程就绪队列);此时线程是活着的;
(3)、运行(running):线程获得CPU资源正在执行任务(执行run()方法);此时除非线程放弃CPU或者有优先级更高的线程进入,线程将一直运行到结束;此时线程是活着的;
(4)、阻塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己的操作(任务执行),即进入阻塞状态;此时线程还是活着的;阻塞原因如下:
  正在休眠:线程调用sleep(long t)方法进入休眠,休眠到指定时间后进入就绪状态;
  正在等待:线程调用wait()方法,可调用notify()方法回到就绪状态;
  被另一个线程所阻塞:调用suspend()方法,可调用resume()方法恢复;
(5)、死亡(dead):当线程执行完毕或被其他线程杀死,线程就进入死亡状态;此时线程不可能再进入就绪状态等待执行;此时线程不是活着的;
  死亡原因如下:
   自然终止:正常运行run()方法后终止;
   异常终止:调用stop()方法让一个线程终止运行;
2、与线程状态对应的常用方法:
run():必须被重写,实现具体的业务功能;
start():启动线程;
sleep():释放CPU执行权,不释放锁;
wait():释放CPU执行权,释放锁;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象锁,只是暂时失去对象锁,wait后(不解)还要返还对象锁;当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在同步块(synchronized block)中调用;
notify()/notifyAll():唤醒在当前对象等待池中等待的第一个线程/所有线程;notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常;
yied():使当前正在运行的线程临时暂停,让出CPU的使用权,让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度,使该线程重新进入Running状态);如果没有同等优先权的线程,那么yied()方法将不会起作用;
3、状态转换:
 新建 -> 就绪:
  start();
 就绪 -> 运行:
  获得CPU执行权;
 运行 -> 就绪:
  yield();
 运行 -> 阻塞:
  sleep()、wait()、join()、synchronized;
 阻塞 -> 就绪:
  sleep()结束、wait()结束、IO完成;
 运行 -> 死亡:
  正常结束、异常退出;

守护线程:
 可以简单的理解为后台运行线程;
 进程结束,守护线程跟着自动结束,不需要手动的去关心和通知其状态;
 Java的垃圾回收是一个守护线程;
 当正在运行的线程都是守护线程时,Java虚拟机退出;
 JRE判断程序是否执行结束的标准是所有的前台运行线程执行完毕了,而不管后台线程的状态;
 当进程中所有非守护线程已结束或退出时,即使仍有守护线程在运行,进程仍将结束;

线程组:

ThreadLocal:
 当前线程副本;
 当使用 ThreadLocal 维护变量的时候,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程对应的副本;从线程的角度看,目标变量就像是线程的本地变量;
 ThreadLocal在处理线程的局部变量的时候比synchronized同步机制解决线程安全问题更简单、更方便,且结果程序拥有更高的并发性;
 注意:使用ThreadLocal,一般都是声明在静态变量(没说局部变量)中,如果不断创建ThreadLocal而且没有调用其remove()方法,将会导致内存泄漏;

技术分享图片
 1 /**
 2  * ThreadLocal的使用
 3  */
 4 package thread01;
 5 
 6 public class ThreadLocalTest01
 7 {
 8     // 通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
 9     // 可以不加static
10     private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>()
11     {
12         // 默认的是protected修饰符
13         // protected Integer initialValue() {};
14         // 重写此方法,返回该线程变量的初始值
15         public Integer initialValue()
16         {
17             return 0;
18         }
19     };
20     
21     // 获得当前变量
22     public ThreadLocal<Integer> getThreadLocal()
23     {
24         return seqNum;
25     }
26     
27     // 实现读取下一个序列值
28     public int getNexNum()
29     {
30         seqNum.set(seqNum.get() + 1);
31         return seqNum.get();
32     }
33     
34     // 定义成员内部类
35     // 这里加static只是为了能在main方法中使用
36     private static class TestClient implements Runnable
37     {
38         private ThreadLocalTest01 tl;
39         
40         public TestClient(ThreadLocalTest01 tl)
41         {
42             this.tl = tl;
43         }
44         
45         @Override
46         public void run()
47         {
48             for(int i=0;i<3;i++)
49             {
50                 // 每个线程打出三个序列值
51                 System.out.println("thread[" + Thread.currentThread().getName() + "] --> tl[" + tl.getNexNum() + "]");
52             }
53             
54             // 每个线程用完的时候要记得删除
55             tl.getThreadLocal().remove();
56         }
57         
58     }
59     
60     public static void main(String[] args)
61     {
62         ThreadLocalTest01 threadLocalTest01 = new ThreadLocalTest01();
63         
64         // 三个线程共享threadLocalTest01,格子产生序列号
65         TestClient testClient1 = new TestClient(threadLocalTest01);
66         TestClient testClient2 = new TestClient(threadLocalTest01);
67         TestClient testClient3 = new TestClient(threadLocalTest01);
68         
69         Thread thread1 = new Thread(testClient1);
70         Thread thread2 = new Thread(testClient2);
71         Thread thread3 = new Thread(testClient3);
72         
73         thread1.start();
74         thread2.start();
75         thread3.start();
76     }
77 }
ThreadLocal的使用

线程的异常处理:
 详解:
  run方法不允许抛出异常,所有的异常必须在run方法中进行处理;就是说run方法中可以抛出异常,但不能往run方法外抛出异常,在run方法里面抛出的异常也必须在run方法内处理掉;
  在run方法中,抛出的已检查异常(checked exception)必须使用try...catch...进行处理,否则报错,编译不通过;(不是在run方法中抛出已检查异常,则既可以使用try...catch...进行处理,也可以使用throws继续往上抛)
  在run方法中,虽然向外抛出未检查异常不会报错,但这样不合理(run方法中的异常应该在run处理);
 线程中处理异常的方法总结:
  不能直接在一个线程中抛出异常;
  如果是已检查异常(checked exception),推荐采用try...catch...块来处理;
  如果是未检查异常(unchecked exception),推荐方法:注册一个实现UncaughtExceptionHandler接口的对象实例来处理;
 线程中,处理未检查异常的方法的具体步骤总结:
  定义一个类实现UncaughtExceptionHandler接口,在需要实现的方法里面包含对异常处理的逻辑和步骤;
  定义一个线程,执行需要的业务逻辑功能;
  在创建和执行该子线程的方法中,在thread.start()语句前增加一个thread.setUncaughtExceptionHandler(自定义异常处理类对象)语句来实现异常处理逻辑的注册;

技术分享图片
  1 /**
  2  * 线程中,对已检查异常的处理
  3  */
  4 package thread01;
  5 
  6 import java.io.FileWriter;
  7 import java.io.IOException;
  8 
  9 public class CheckedExceptionTest
 10 {
 11     public static void main(String[] args)
 12     {
 13         ThreadDemo09 threadDemo09 = new ThreadDemo09();
 14         Thread thread = new Thread(threadDemo09);
 15         thread.start();
 16     }
 17 }
 18 
 19 class ThreadDemo09 implements Runnable
 20 {
 21     @Override
 22     public void run()
 23     {
 24         // 不能在try中声明,这样会导致finally中无法只用;
 25         FileWriter fw = null;
 26         
 27         try
 28         {
 29             // 在线程的run()方法中,对于抛出的已检查异常,必须且只能使用try...catch...在run方法内部进行处理,不处理则直接报错(编译不过)
 30             // 抛出异常的原因:可能找不到文件路径;
 31             fw = new FileWriter("F:/ppt/text.txt");
 32         } 
 33         catch (IOException e1)
 34         {
 35             e1.printStackTrace();
 36         }  
 37         finally 
 38         {
 39             // 无论程序怎么运行,最后都要关闭流; 
 40             // 一旦路径有误,fw就会出现空指针异常,所以要先判断fw是否为空  
 41             if(null != fw)
 42             {
 43                 try
 44                 {
 45                     fw.close();
 46                 } 
 47                 catch (IOException e)
 48                 {
 49                     e.printStackTrace();
 50                 }
 51             }
 52         }
 53     }
 54     
 55     public void testCheckedException()
 56     {
 57         // 非run方法中抛出的已检查异常,既可以使用try...catch...进行捕获,也可以继续抛出;
 58         // 以下是使用try...catch...进行捕获处理
 59         
 60         // 不能在try中声明,这样会导致finally中无法只用;
 61         FileWriter fw = null;
 62         
 63         try
 64         {
 65             // 对于抛出的已检查异常,必须且只能使用try...catch...在run方法内部进行处理,不处理则直接报错(编译不过)
 66             // 抛出异常的原因:可能找不到文件路径;
 67             fw = new FileWriter("F:/ppt/text.txt");
 68         } 
 69         catch (IOException e1)
 70         {
 71             e1.printStackTrace();
 72         }  
 73         finally 
 74         {
 75             // 无论程序怎么运行,最后都要关闭流; 
 76             // 一旦路径有误,fw就会出现空指针异常,所以要先判断fw是否为空  
 77             if(null != fw)
 78             {
 79                 try
 80                 {
 81                     fw.close();
 82                 } 
 83                 catch (IOException e)
 84                 {
 85                     e.printStackTrace();
 86                 }
 87             }
 88         }
 89     }
 90     
 91     public void testCheckedException2() throws IOException  
 92     {
 93         // 非run方法中抛出的已检查异常,既可以使用try...catch...进行捕获,也可以继续抛出;
 94         // 以下是使用 throws 继续往外抛
 95         FileWriter fw = new FileWriter("F:/ppt/text.txt");
 96         
 97         if(null != fw)
 98         {
 99             fw.close();
100         }
101     }
102     
103 }
线程中,对已检查异常的处理
技术分享图片
 1 /**
 2  * 线程中,对未检查异常的处理
 3  */
 4 package thread01;
 5 
 6 import java.lang.Thread.UncaughtExceptionHandler;
 7 
 8 public class UncheckedExceptionTest
 9 {
10     public static void main(String[] args)
11     {
12         ThreadDemo10 threadDemo10 = new ThreadDemo10();
13         Thread thread = new Thread(threadDemo10);
14         thread.setUncaughtExceptionHandler(new UncheckedExcepionHandlerDemo());
15         thread.start();
16     }
17 }
18 
19 class ThreadDemo10 implements Runnable
20 {
21     // 在线程的run方法中,对于未检查异常:可以不处理;可以继续往外抛;也可以使用try..catch..进行捕获处理;
22     @Override
23     public void run() // throws NumberFormatException
24     {
25         // 抛出类型转换异常
26         int result = Integer.parseInt("ijn");
27         System.out.println(result);
28     }
29     
30     public void testUncheckedException1()
31     {
32         // 在非run()方法中,对于未检查异常:可以不处理;可以继续往外抛;也可以使用try..catch..进行捕获处理;
33         // 以下是不处理
34         int result = Integer.parseInt("ijn");
35         System.out.println(result);
36     }
37     
38     public void testUncheckedException2()
39     {
40         // 在非run()方法中,对于未检查异常:可以不处理;可以继续往外抛;也可以使用try..catch..进行捕获处理;
41         // 以下通过try..catch..进行捕获处理
42         int result = 0;
43         try
44         {
45             result = Integer.parseInt("ijn");
46         } 
47         catch (NumberFormatException e)
48         {
49             e.printStackTrace();
50         }
51         System.out.println(result);
52     }
53     
54     public void testUncheckedException3() throws NumberFormatException
55     {
56         // 在非run()方法中,对于未检查异常:可以不处理;可以继续往外抛;也可以使用try..catch..进行捕获处理;
57         // 以下是使用 throws 继续往外抛
58         int result = Integer.parseInt("ijn");
59         System.out.println(result);
60     }
61 }
62 
63 class UncheckedExcepionHandlerDemo implements UncaughtExceptionHandler
64 {
65     @Override
66     public void uncaughtException(Thread t, Throwable e)
67     {
68         System.out.println("一个未检查异常被捕获...");
69         System.out.println("线程id:" + t.getId());
70         System.out.println("异常名称:" + e.getClass().getName() + ",异常信息:" + e.getMessage());
71         System.out.println("异常栈信息:");
72         e.printStackTrace(System.out);
73         System.out.println("线程状态:" + t.getState());
74     }
75 }
线程中,对未检查异常的处理










































































以上是关于Java并发编程从入门到精通 - 第2章:认识Thread的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程从入门到精通 - 第6章:线程池

Java并发编程从入门到精通 - 第7章:Fork/Join框架

Java并发编程从入门到精通 - 第3章:Thread安全

Java并发编程从入门到精通 - 第5章:多线程之间的交互:线程阀

Java并发编程从入门到精通 张振华.Jack --我的书

Java 从入门到精通(第4版)第6章 数组