1. 程式人生 > >java多線程——多線程的安全問題

java多線程——多線程的安全問題

one alt strong 如果 void align 讓其 adl 類名.class

java多線程——多線程的安全問題

模擬火車票售票程序:

 1 /**
 2  * 售票案例:售票的動作被多個線程同時執行
 3  */
 4 class Ticket implements Runnable{
 5     //描述票的數量
 6     private int tickets=100;
 7     //售票,線程任務中通常都有循環結構
 8     @Override
 9     public void run() {
10         while(true) {
11             if(tickets>0) {//此處可能線程1,2,3,4都進入,那麽就可能出現輸出tickets為負值的錯誤數據
12 try { 13 Thread.sleep(200); 14 } catch (InterruptedException e) { 15 //待處理 16 } 17 System.out.println(Thread.currentThread().getName()+"------"+tickets--); 18 } 19 } 20 }
21 } 22 23 public class TicketSellDemo { 24 public static void main(String[] args) { 25 Ticket t=new Ticket(); 26 27 Thread t1=new Thread(t); 28 Thread t2=new Thread(t); 29 Thread t3=new Thread(t); 30 Thread t4=new Thread(t); 31 32 t1.start();
33 t2.start(); 34 t3.start(); 35 t4.start(); 36 } 37 }

1、線程安全問題出現的原因:

(1)多個線程操作共享的數據;

(2)線程任務操作共享數據的代碼有多條(多個運算)。

2、解決思路:

只要讓一個線程在執行線程任務時將多條操作共享數據的代碼執行完,在執行過程中,不要讓其他線程參與運算。那麽如何在代碼中體現呢?java中解決此問題:

(1)通過代碼塊完成,這個代碼塊叫做同步代碼塊,使用關鍵字synchronized

技術分享圖片
 1 /**
 2  * 售票案例:售票的動作被多個線程同時執行
 3  */
 4 class Ticket implements Runnable{
 5     //描述票的數量
 6     private int tickets=20;
 7     //售票,線程任務中通常有循環結構
 8     private Object obj=new Object();//用來處理安全問題
 9     @Override
10     public void run() {
11         while(true) {
12             synchronized(obj)
13             {
14                 if(tickets>0) {
15                     try { Thread.sleep(200);} catch (InterruptedException e) {/*待處理*/}
16                     System.out.println(Thread.currentThread().getName()+"------"+tickets--);
17                 }    
18             }
19         }
20     }
21 }
22 
23 public class TicketSellDemo {
24     public static void main(String[] args) {
25         Ticket t=new Ticket();
26         
27         Thread t1=new Thread(t);
28         Thread t2=new Thread(t);
29         Thread t3=new Thread(t);
30         Thread t4=new Thread(t);
31         
32         t1.start();
33         t2.start();
34         t3.start();
35         t4.start();
36     }
37 }
View Code

同步的前提:必須保證多個線程中使用的是同一個鎖。例如:若將上述代碼中synchronized(obj)改為synchronized(new Object()),安全問題仍然不能解決。

(2)使用同步函數(方法)

同步函數使用的鎖是this,靜態同步函數使用的鎖是字節碼文件對象,類名.class.

技術分享圖片
 1 /**
 2  * 售票案例:售票的動作被多個線程同時執行
 3  */
 4 class Ticket implements Runnable{
 5     //描述票的數量
 6     private int tickets=20;
 7     //售票,線程任務中通常有循環結構
 8     @Override
 9     public void run() {
10         //method1();
11         while(true) {
12             sale();
13         }
14     }
15     
16     public synchronized void sale() {
17             if(tickets>0) {
18             //    try { Thread.sleep(200);} catch (InterruptedException e) {/*待處理*/}
19                 System.out.println(Thread.currentThread().getName()+"------"+tickets--);
20             }    
21     }
22 }
23 
24 public class TicketSellDemo {
25     public static void main(String[] args) {
26         Ticket t=new Ticket();
27         
28         Thread t1=new Thread(t);
29         Thread t2=new Thread(t);
30         Thread t3=new Thread(t);
31         Thread t4=new Thread(t);
32         
33         t1.start();
34         t2.start();
35         t3.start();
36         t4.start();
37     }
38 }
View Code

同步函數和同步代碼塊有什麽區別?

同步函數使用的鎖是固定的this,同步代碼塊使用的鎖可以是任意對象。當線程任務只需要一個同步時完全可以使用同步函數。當線程任務中需要多個同步時,必須通過鎖來區分,這時必須使用同步代碼塊。同步代碼塊較為常用。

3、單例懶漢式的並發訪問

技術分享圖片
 1 //餓漢式,多線程並發沒有問題。
 2 /*
 3   public class Single {
 4         private static final Single instance=new Single();
 5         private Single() {};
 6         public static Single getInstance() {
 7             return instance;
 8         }
 9     }
10 */
11 
12 //懶漢式:存在線程安全問題,需要加上同步機制synchronized
13 /*
14 public class Single {
15     private static Single instance=null;
16     private Single() {}
17     public static synchronized Single getInstance() {
18         if(instance==null) {
19             instance=new Single();
20         }
21         return instance;
22     }    
23 }
24 */
25 //但是,同步的出現降低了效率.可以通過雙重判斷,解決效率問題,減少判斷次數
26 public class Single {
27     private static Single instance=null;
28     private Single() {}
29     public static Single getInstance() {
30         if(instance==null) {
31             synchronized(Single.class){
32                 if(instance==null) {
33                     instance=new Single();
34                 }
35             }
36         }
37         return instance;
38     }    
39 }
Single

4、死鎖

同步的另一個弊端:死鎖

情況之一:當線程任務中出現了多個同步(多個鎖)時,如果同步中嵌套了其他的同步,這時容易引發死鎖。

技術分享圖片
 1 class Test implements Runnable{
 2     private boolean flag;
 3     Test(boolean flag){
 4         this.flag=flag;
 5     }
 6     
 7     public void run() {
 8         if(flag) {
 9             while(true) {//加了循環一定會鎖住
10                 synchronized(Mylock.lockA) {
11                     System.out.println(Thread.currentThread().getName()+"....if...lockA");
12                     synchronized(Mylock.lockB) {
13                         System.out.println(Thread.currentThread().getName()+"....if...lockB");
14                     }
15                 }
16             }
17             
18         }else {
19             while(true) {
20                 synchronized(Mylock.lockB) {
21                     System.out.println(Thread.currentThread().getName()+"....else....lockB");
22                     synchronized(Mylock.lockA) {
23                         System.out.println(Thread.currentThread().getName()+"....else.....lockA");
24                     }
25                 }
26             }
27         }
28     }
29 }
30 
31 class Mylock{
32     public static final Object lockA=new Object();
33     public static final Object lockB=new Object();
34 }
35 public class Deadlocks {
36     public static void main(String[] args) {
37         Test t1=new Test(true);
38         Test t2=new Test(false);
39         Thread t11=new Thread(t1);
40         Thread t22=new Thread(t2);
41         t11.start();
42         t22.start();
43     }
44     
45     
46 }
Deadlocks

java多線程——多線程的安全問題