Java單線程多實例和多線程多實例


  最近寫了一個程序,是采用多線程往redis里面寫入數據,想統計一下一共寫了多少條數據,於是用了一個static的全局變量count來累加,這塊代碼抽象出來就是這樣的:

 1 public class MultiThread implements Runnable {
 2     private String name;
 3     private static Integer count = 0;
 4 
 5     public MultiThread() {
 6     }
 7 
 8     public MultiThread(String name) {
 9         this.name = name;
10     }
11 
12     public void run() {
13         for (int i = 0; i < 5; i++) {
14             //模擬寫入redis的IO操作消耗時間
15             try {
16                 Thread.sleep(200);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             
21             //累加寫入次數
22             count++;
23             System.out.println(name + "運行        " + i + "    寫入條數:" + count);
24         }
25     }
26     
27     public static void main(String[] args) {
28          for (int i = 0; i < 100; i++) {
29              new Thread(new MultiThread("Thread"+i)).start();
30          }
31     }
32 }

啟動了100個線程,每個線程寫入5次,預計結果應該是500,但是實際結果是這樣的:

分析了原因,應該是因為count++不是原子操作,這句代碼實際上是執行了3步操作:1,獲取類變量count值。2,count+1。3,將count+1后的結果賦值給類變量count。在這3步中間都有可能中斷執行其他線程。這樣比如線程1先獲取了count=0,這時候切換到線程2,線程2獲取了count=0,然后又切換到線程1,線程1執行count++=1並修改了類變量count=1,之后又切換到線程2,線程2對之前它獲取到的count=0執行count++=1並修改類變量count=1。問題出現了,明明有兩個線程對count累加了兩次,但是由於count沒有加鎖,最終類變量只加了1。

根據分析修改程序成下面這樣,給count加了同步,將上面代碼中第22行的"count++"改為了:

1 synchronized (count) {
2   count++;
3}

這次運行前兩次都正常顯示了500,但是多運行幾次發現個別時候仍然有問題:

再次分析原因,分析不出來了,開始各種修改各種試,終於成功試驗出了正確代碼,將count++移到外面,封裝到類的靜態同步方法里:

 1 public class MultiThread implements Runnable {
 2     private String name;
 3     private static Integer count = 0;
 4 
 5     public MultiThread() {
 6     }
 7 
 8     public MultiThread(String name) {
 9         this.name = name;
10     }
11 
12     public void run() {
13         for (int i = 0; i < 5; i++) {
14             //模擬寫入redis的IO操作消耗時間
15             try {
16                 Thread.sleep(200);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             
21             //累加寫入次數
22             countPlus();
23             System.out.println(name + "運行        " + i + "    寫入條數:" + count);
24         }
25     }
26     
27     private static synchronized void countPlus(){
28         count++;
29     }
30     
31     
32     public static void main(String[] args) {
33          for (int i = 0; i < 100; i++) {
34              new Thread(new MultiThread("Thread"+i)).start();
35          }
36     }
37 }

這次運行多次結果均是正常的,為了確保結果正確,又把線程數改為1000試驗了多次,結果也是正確的(5000,不過要好好找找了,因為countPlus()和sysout在多個線程里會交錯執行,這個5000不一定會出現在什么位置...從最后一行往前找吧...)。

看着這個運行結果,基本能猜到原因了,原因就出在這一句:

1 new Thread(new MultiThread("Thread"+i)).start();

這里為每個線程new了一個對象,所以之前的

1 synchronized (count) {
2     count++;
3 }

的作用范圍是同一個對象的多個線程,也就是說它能夠確保Thread1對象的多個線程訪問count的時候是同步的,而實際上我們是多線程多實例,每個線程都對應一個不同的對象,所以這句代碼實際上是不能起到同步count的作用的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM