1. 线程控制

    1) 启动线程

        通过调用 Thread 类的 start 方法来启动一个线程,这时此线程是处于就绪状态,并没有运行。
        
        得到 CPU 时间片后,线程就开始自动执行 run 方法,run 方法被称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。
    
        run 方法当作普通方法的方式调用。程序还是要顺序执行,要等待 run 方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

    2) 暂停、恢复和停止线程

        暂停(suspend)、恢复(resume)和停止(stop),这些 API 是已过期的,不建议使用。
        
        不建议使用的原因:

            (1) 以 suspend 方法为例,在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题;
            (2) stop 方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下;
            (3) 因为 suspend、resume 和 stop 方法带来的副作用,这些方法才被标注为不建议使用的过期方法;

    3) 中断线程

        (1) 使用退出标志

            当 run 方法执行完后,线程就会退出。但有时 run 方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。

            在这种情况下,一般是将这些任务放在一个循环中,如 while 循环。如果想使 while 循环在某一特定条件下退出,最直接的方法就是设一个 boolean 类型的标志,并通过设置这个标志为 true 或 false来控制 while 循环是否退出。

        (2) 使用 interrupt 方法

            interrupt 方法中断线程:设置线程的中断状态位为 true,线程不时地检测中断标示位,判断线程是否应该被中断 (中断标示值是否为 true)。

            interrupt 方法只是改变中断状态,不会中断一个正在运行的线程,需要用户自己去监视线程的状态为并做处理。

            这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。

            更确切的说,如果线程被Object.wait, Thread.join 和 Thread.sleep 三种方法之一阻塞,此时调用该线程的 interrupt() 方法,那么该线程将抛出一个 InterruptedException 中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到 wait()、 sleep() 或 join() 时,才会抛出 InterruptedException。

            a) 使用 interrupt() + InterruptedException 来中断线程:

                线程处于阻塞状态,如Thread.sleep、wait、IO阻塞等情况时,调用interrupt方法后,sleep等方法将会抛出一个InterruptedException。

            b) 使用 interrupt() + isInterrupted() 来中断线程:

                this.interrupted(): 测试当前线程是否已经中断(静态方法)。如果连续调用该方法,则第二次调用将返回false。在api文档中说明 interrupted()方法具有清除状态的功能。执行后具有将状态标识清除为false的功能。

              this.isInterrupted(): 测试线程是否已经中断,但是不能清除状态标识。

    4) join 线程

        join 方法是 Thread类 提供的让一个线程等待另一个线程完成的方法。格式:

         

thread.start();
            thread.join();

    5) 后台线程

        后台线程(Daemon Thread)是在后台运行的,它的任务是为其他的线程提供服务,也被称为守护线程或精灵线程。后台线程的特征:如果所有的前台线程都死亡,后台线程会自动死亡。

        调用 Thread 对象的 setDaemon(true) 方法可以将一个指定的线程设置为后台线程。格式:

       

thread.setDaemon(true);
            thread.start();

    6) 线程睡眠

        线程调用 sleep() 方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行。

        Thread 类为睡眠线程提供了两种方法:

public static void sleep(long miliseconds)
            public static void sleep(long miliseconds,int nanos)



        参数声明:
            
      

miliseconds - 以毫秒为单位的睡眠时间。
            nanos - 这是 0-999999 额外纳秒的睡眠时间。

 

    7) 线程让步

        线程让步 yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

        但是,实际中无法保证 yield() 达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

    8) sleep() 和 yield()的区别:

        (1) sleep() 方法让线程进入阻塞状态,其他所有处于就绪状态的线程都有机会执行,yield() 方法会让线程重新回到就绪状态,但是只有优先级等于或者大于他的线程才会被执行;
        (2) 使用 sleep() 方法需要捕获异常,yield() 不需要;
        (3) sleep() 方法比 yield() 方法有更好的移植性,不建议使用yield();

    实例:

1         public class App {
  2             public static volatile boolean stopFlag = false;
  3 
  4             public static void main( String[] args ) {
  5 
  6                 // 使用退出标志中断线程
  7                 testFlagExit();
  8                 System.out.println("----------------------------------------");
  9 
 10                 // 或使用 interrupt() + InterruptedException 中断线程
 11                 testInterruptExit();
 12                 System.out.println("----------------------------------------");
 13 
 14                 // 使用 interrupt() + isInterrupted() 中断线程
 15                 testIsInterruptedExit();
 16                 System.out.println("----------------------------------------");
 17 
 18                 // Thread join
 19                 ThreadJoinRunnable threadJoin1 = new ThreadJoinRunnable("Thread Join 1");
 20                 ThreadJoinRunnable threadJoin2 = new ThreadJoinRunnable("Thread Join 2");
 21                 ThreadJoinRunnable threadJoin3 = new ThreadJoinRunnable("Thread Join 3");
 22                 try {
 23                     threadJoin1.start();
 24                     threadJoin1.join();
 25                     threadJoin2.start();
 26                     threadJoin2.join();
 27                     threadJoin3.start();
 28                     threadJoin3.join();
 29                 }  catch (InterruptedException e) {
 30                     e.printStackTrace();
 31                 }
 32 
 33             }
 34 
 35             // 使用退出标志中断线程
 36             public static void testFlagExit() {
 37 
 38                 Thread t = new Thread(new Runnable() {
 39                     public void run() {
 40                         String name = Thread.currentThread().getName();
 41                         int i = 1;
 42                         try {
 43 
 44                             for (; i <= 10; i++) {
 45                                 if (stopFlag) {
 46                                     break;
 47                                 }
 48                                 System.out.println(name + ": (" + i + ") stopFlag = " + stopFlag);
 49                                 Thread.sleep(1000);
 50                             }
 51                         } catch (InterruptedException e) {
 52                             System.out.println(name + ": interrupted");
 53                         }
 54                         System.out.println(name + ": (" + i + ") stopFlag = " + stopFlag + ", exit");
 55                     }
 56                 }, "Thread Flag"  );
 57                 t.start();
 58 
 59                 try {
 60                     Thread.sleep(5000);
 61 
 62                     System.out.println("main: stopping " + t.getName() + ", set stopFlag = true");
 63                     stopFlag = true;
 64 
 65                     Thread.sleep(1000);
 66 
 67                 }  catch (InterruptedException e) {
 68                     e.printStackTrace();
 69                 }
 70 
 71             }
 72 
 73             // 或使用 interrupt() + InterruptedException 中断线程
 74             public static void testInterruptExit() {
 75                 Thread t = new Thread(new Runnable() {
 76                     public void run() {
 77                         String name = Thread.currentThread().getName();
 78                         try {
 79 
 80                             for (int i = 1; i <= 10; i++) {
 81                                 System.out.println(name + ": (" + i + ")");
 82                                 Thread.sleep(1000);
 83                             }
 84                         } catch (InterruptedException e) {
 85                             System.out.println(name + ": interrupted");
 86                         }
 87                         System.out.println(name +": exit");
 88                     }
 89                 }, "Thread Interrupt"  );
 90                 t.start();
 91 
 92                 try {
 93                     Thread.sleep(5000);
 94 
 95                     System.out.println("main: stopping " + t.getName() + ", call interrupt()");
 96                     t.interrupt();
 97 
 98                     Thread.sleep(1000);
 99                 }  catch (InterruptedException e) {
100                     e.printStackTrace();
101                 }
102             }
103 
104             // 使用 interrupt() + isInterrupted() 中断线程
105             public static void testIsInterruptedExit() {
106                 Thread t = new Thread(new Runnable() {
107                     public void run() {
108                         String name = Thread.currentThread().getName();
109                         Boolean b = Thread.currentThread().isInterrupted();
110                         int i = 1;
111 
112                         while(!b) {
113                             System.out.println(name + ": isInterrupted(" + i + ") = " + b);
114                             b = Thread.currentThread().isInterrupted();
115                             i++;
116                         }
117 
118                         System.out.println(name +": isInterrupted(" + i + ") = " + b + ", exit");
119                     }
120                 }, "Thread IsInterrupted"  );
121                 t.start();
122 
123                 try {
124                     Thread.sleep(1);
125 
126                     System.out.println("main: stopping " + t.getName() + ", call interrupt()");
127                     t.interrupt();
128 
129                     Thread.sleep(1000);
130                 }  catch (InterruptedException e) {
131                     e.printStackTrace();
132                 }
133             }
134         }
135 
136         class ThreadJoinRunnable extends Thread implements Runnable {
137 
138             ThreadJoinRunnable(String name) {
139                 this.setName(name);
140             }
141 
142             public void run() {
143                 try {
144                     System.out.println(getName());
145                     Thread.sleep(1000);
146                 } catch (InterruptedException e) {
147                     System.out.println(getName() + ": interrupted");
148                 }
149                 System.out.println(getName() + ": exit");
150             }
151         }

    输出:

    

Thread Flag: (1) stopFlag = false
        Thread Flag: (2) stopFlag = false
        Thread Flag: (3) stopFlag = false
        Thread Flag: (4) stopFlag = false
        Thread Flag: (5) stopFlag = false
        main: stopping Thread Flag, set stopFlag = true
        Thread Flag: (6) stopFlag = true, exit
        ----------------------------------------
        Thread Interrupt: (1)
        Thread Interrupt: (2)
        Thread Interrupt: (3)
        Thread Interrupt: (4)
        Thread Interrupt: (5)
        main: stopping Thread Interrupt, call interrupt()
        Thread Interrupt: interrupted
        Thread Interrupt: exit
        ----------------------------------------
        Thread IsInterrupted: isInterrupted(1) = false
        Thread IsInterrupted: isInterrupted(2) = false
        Thread IsInterrupted: isInterrupted(3) = false
        ...

        Thread IsInterrupted: isInterrupted(19) = false
        Thread IsInterrupted: isInterrupted(20) = false
        main: stopping Thread IsInterrupted, call interrupt()
        Thread IsInterrupted: isInterrupted(21) = false
        Thread IsInterrupted: isInterrupted(22) = true, exit
        ----------------------------------------
        Thread Join 1
        Thread Join 1: exit
        Thread Join 2
        Thread Join 2: exit
        Thread Join 3
        Thread Join 3: exit

2. 线程死锁

    线程死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

    下面我们通过一些实例来说明死锁现象。

    生活中的一个实例:两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。

    1) 死锁产生的原因

        (1) 系统资源的竞争

            通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

        (2) 进程推进顺序非法

            进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都会因为所需资源被占用而阻塞。   

    2)死锁发生时的条件:

        (1) 互斥条件:一个资源每次只能被一个进程使用。独木桥每次只能通过一个人。
        (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。乙不退出桥面,甲也不退出桥面。
        (3) 不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺。甲不能强制乙退出桥面,乙也不能强制甲退出桥面。
        (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。如果乙不退出桥面,甲不能通过,甲不退出桥面,乙不能通过。

    3) 如何避免死锁

        在有些情况下死锁是可以避免的。下面介绍三种用于避免死锁的技术:

        (1) 加锁顺序(线程按照一定的顺序加锁)
        (2) 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
        (3) 死锁检测   

    Java 中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞,导致死锁。

    这是最容易理解也是最简单的死锁的形式。但是实际环境中的死锁往往比这个复杂的多。可能会有多个线程形成了一个死锁的环路,比如:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1,这样导致了一个锁依赖的环路:T1依赖T2的锁L2,T2依赖T3的锁L3,而T3依赖T1的锁L1,从而导致了死锁。

        
    实例:

1         public class App {
 2             public static void main( String[] args ) {
 3 
 4                 // 死锁的情况1
 5                 Thread t1 = new Thread(new DeadLock(true), "Thread DeadLock 1");
 6                 Thread t2 = new Thread(new DeadLock(false), "Thread DealLock 2");
 7 
 8                 // 死锁的情况2
 9                 //Thread t1 = new Thread(new DeadLock(false), "Thread DeadLock 1");
10                 //Thread t2 = new Thread(new DeadLock(true), "Thread DealLock 2");
11 
12                 // 不死锁的情况1
13                 //Thread t1 = new Thread(new DeadLock(true), "Thread DeadLock 1");
14                 //Thread t2 = new Thread(new DeadLock(true), "Thread DealLock 2");
15 
16                 // 不死锁的情况2
17                 //Thread t1 = new Thread(new DeadLock(false), "Thread DeadLock 1");
18                 //Thread t2 = new Thread(new DeadLock(false), "Thread DealLock 2");
19 
20                 t1.start();
21                 t2.start();
22 
23             }
24         }
25 
26         class DeadLock implements Runnable{
27 
28             private static Object obj1 = new Object();
29             private static Object obj2 = new Object();
30             private boolean flag;
31 
32             public DeadLock(boolean flag){
33                 this.flag = flag;
34             }
35 
36             @Override
37             public void run(){
38                 String name = Thread.currentThread().getName();
39                 System.out.println(name + ":running ...");
40 
41                 /*
42                  * 同时开启线程1和线程2:
43                  * 1. 如果让线程1执行 "代码1"(flag==true),让线程2执行 "代码2" (flag==false), 会死锁;
44                  * 2. 如果让线程1执行 "代码2"(flag==false),让线程2执行 "代码1" (flag==true), 会死锁;
45                  * 3. 如果让线程1执行 "代码1"(flag==true),让线程2执行 "代码1" (flag==true), 不会死锁;
46                  * 4. 如果让线程1执行 "代码2"(flag==false),让线程2执行 "代码2" (flag==false), 不会死锁;
47                  */
48                 if (flag) {
49                     // 代码1
50                     synchronized(obj1){
51                         System.out.println(name + ": lock obj1");
52                         System.out.println(name + ": try to lock obj2 ...");
53                         try {
54                             Thread.sleep(1000);
55                         } catch (InterruptedException e) {
56                             e.printStackTrace();
57                         }
58                         synchronized(obj2){
59                             // 死锁时执行不到这里
60                             System.out.println(name +": after 1 second, lock obj2");
61                         }
62                     }
63                 } else {
64                     // 代码2
65                     synchronized(obj2){
66                         System.out.println(name + ": lock obj2");
67                         System.out.println(name + ": try to lock obj1 ...");
68                         try {
69                             Thread.sleep(1000);
70                         } catch (InterruptedException e) {
71                             e.printStackTrace();
72                         }
73                         synchronized(obj1){
74                             // 死锁时执行不到这里
75                             System.out.println(name +": after 1 second, lock obj1");
76                         }
77                     }
78                 }
79             }
80          }

    
    输出:
  
  

Thread DeadLock 1:running ...
        Thread DeadLock 1: lock obj1
        Thread DeadLock 1: try to lock obj2 ...
        Thread DealLock 2:running ...
        Thread DealLock 2: lock obj2
        Thread DealLock 2: try to lock obj1 ...
        // 此时程序死锁