1. 程式人生 > 實用技巧 >多執行緒-3

多執行緒-3

JAVA執行緒

執行緒的安全問題:

舉個栗子:

售貨員買票:三個售貨員同時買票,並且票數一定。

public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable {
        //總票數
        private
int count = 10; @Override public void run() { while (true) { if (count > 0) { //賣票 System.out.println("正在準備買票"); try { Thread.sleep(1000);//時間差,導致其他執行緒進入 } catch
(InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName() + ":出票成功,剩餘:" + count); //打印出-1,-2 } else { break; } } } }
正在準備買票
正在準備買票
正在準備買票
Thread-0:出票成功,剩餘:7
正在準備買票
Thread-1:出票成功,剩餘:8
正在準備買票
Thread-2:出票成功,剩餘:8
正在準備買票
Thread-2:出票成功,剩餘:6
正在準備買票
Thread-0:出票成功,剩餘:4
正在準備買票
Thread-1:出票成功,剩餘:5
正在準備買票
Thread-1:出票成功,剩餘:3
正在準備買票
Thread-2:出票成功,剩餘:2
正在準備買票
Thread-0:出票成功,剩餘:2
正在準備買票
Thread-1:出票成功,剩餘:1
正在準備買票
Thread-2:出票成功,剩餘:0
Thread-0:出票成功,剩餘:-1
Thread-1:出票成功,剩餘:-2

出現負數的票數。這就是執行緒不安全的情況,執行緒休眠的情況導致其他執行緒會趁虛而入(搶佔式排程嘛)。

如何解決:1,使用同步程式碼塊

 1   public static void main(String[] args) {
 2         //執行緒不安全
 3         //解決方案1:同步程式碼塊
 4         /*
 5         Object o = new Object();
 6         格式: synchronized(鎖物件){
 7         }
 8          */
 9         Runnable run = new Ticket();
10         new Thread(run).start();
11         new Thread(run).start();
12         new Thread(run).start();
13     }
14     static class Ticket implements Runnable {
15         //總票數
16         private int count = 10;
17         private Object o = new Object();//同一把鎖,即公鎖
18         @Override
19         public void run() {
20            //Object o = new Object();//如果將鎖物件new在這裡,導致每個執行緒都有自己的鎖,這樣還是會發生執行緒不安全,因為每個執行緒只關注自己的鎖。注意!!
21             while (true) {
22                 synchronized (o) {
23                     if (count > 0) {
24                         //賣票
25                         System.out.println("正在準備買票");
26                         try {
27                             Thread.sleep(1000);//時間差,導致其他執行緒進入
28                         } catch (InterruptedException e) {
29                             e.printStackTrace();
30                         }
31                         count--;
32                         System.out.println(Thread.currentThread().getName() + ":出票成功,剩餘:" + count);                  
34                     } else {
35                         break;
36                     }
37                 }
38             }
39         }
40     }

2、同步方法

 1  public static void main(String[] args) {
 2         //執行緒不安全
 3         //解決方案2:同步方法
 4 
 5         Runnable run = new Ticket();
 6         new Thread(run).start();
 7         new Thread(run).start();
 8         new Thread(run).start();
 9 
10         /*new Thread(new Ticket()).start();
11         new Thread(new Ticket()).start();
12         new Thread(new Ticket()).start();
13         這樣相當於沒鎖,注意!!*/
14     }
15     static class Ticket implements Runnable {
16         //總票數
17         private int count = 10;
18         //給方法新增synchronized
19         public  synchronized boolean sale(){
22             if (count > 0) {
23                 //賣票
24                 System.out.println("正在準備買票");
25                 try {
26                     Thread.sleep(1000);//時間差,導致其他執行緒進入
27                 } catch (InterruptedException e) {
28                     e.printStackTrace();
29                 }
30                 count--;
31                 System.out.println(Thread.currentThread().getName()+":出票成功,剩餘:" + count);
32                 return true;
33             }else {
34                 return false;
35             }
36         }
37         @Override
38         public void run() {
39             while (true) {
40                 boolean flag =  sale();
41                 if(!flag){
42                     break;
43                 }
44             }
45         }
46     }
正在準備買票
Thread-0:出票成功,剩餘:9
正在準備買票
Thread-0:出票成功,剩餘:8
正在準備買票
Thread-0:出票成功,剩餘:7
正在準備買票
Thread-0:出票成功,剩餘:6
正在準備買票
Thread-0:出票成功,剩餘:5
正在準備買票
Thread-0:出票成功,剩餘:4
正在準備買票
Thread-0:出票成功,剩餘:3
正在準備買票
Thread-0:出票成功,剩餘:2
正在準備買票
Thread-0:出票成功,剩餘:1
正在準備買票
Thread-0:出票成功,剩餘:0

Process finished with exit code 0

兩種方法結果如上圖。

3、顯示鎖 Lock

 1 public static void main(String[] args) {
 2         //執行緒不安全
 3         //解決方案3:顯示鎖 Lock 子類 ReentrantLock
 4         Runnable run = new Ticket();
 5         new Thread(run).start();
 6         new Thread(run).start();
 7         new Thread(run).start();
 8     }
 9     static class Ticket implements Runnable {
10         //總票數
11         private int count = 10;
12         //顯示鎖 :fair引數為true 就表示公平鎖
13         private Lock l = new ReentrantLock(true);
14         @Override
15         public void run() {
16             while (true) {
17                 l.lock();
18                 if (count > 0) {
19                     //賣票
20                     System.out.println("正在準備買票");
21                     try {
22                         Thread.sleep(1000);//時間差,導致其他執行緒進入
23                     } catch (InterruptedException e) {
24                         e.printStackTrace();
25                     }
26                     count--;
27                     System.out.println(Thread.currentThread().getName()+":出票成功,剩餘:" + count);
28                 }else {
29                     break;
30                 }
31                 l.unlock();
32             }
33         }
34     }

記得用完要解鎖unlock()。(公平鎖:先到先得,公平嘛)。