1. 數據不共享的情況
在探討數據共享的話題前,先來看看數據不共享的情況,每一個線程裏面的數據都是獨立的,就像下面的例子,3個線程,每一個線程自己對自己的數據進行扣減,直到0爲止
代碼demo:
public class TestThread {
public static void main(String[] args) {
MyThread myThread_A = new MyThread("A");
MyThread myThread_B = new MyThread("B");
MyThread myThread_C = new MyThread("C");
myThread_A.start();
myThread_B.start();
myThread_C.start();
}
}
class MyThread extends Thread {
private int count = 5;
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
// super.run();
while (count != 0) {
System.out.println(name + " = " + count);
count--;
}
}
}
2. 數據共享的情況
這裏先看一段測試代碼,看看共享數據的結構,按照理想的情況下,MyRunnable只實例了一次,所以裏面的count=5將會被扣減5次,打印的結果應該是,5,4,3,2,1,因爲這裏有5個線程,每次調用run的時候,都會減去1,但是結果,確實有點出乎意外....而且每一次執行的結果都不一樣
public class TestThread {
public static void main(String[] args) {
//新建一個帶有Runnable接口的類
MyRunnable myRunnable = new MyRunnable();
//新建5個線程,但使用了同一個Runnable實例對象,意味着裏面的數據是共享的
//這裏的Thread(Runnable,String)是一個構造函數,第一次參數爲Runnable接口,第二個爲線程名稱
Thread thread_A = new Thread(myRunnable,"A");
Thread thread_B = new Thread(myRunnable,"B");
Thread thread_C = new Thread(myRunnable,"C");
Thread thread_D = new Thread(myRunnable,"D");
Thread thread_E = new Thread(myRunnable,"E");
thread_A.start();
thread_B.start();
thread_C.start();
thread_D.start();
thread_E.start();
}
}
class MyRunnable implements Runnable {
private int count = 5;
public MyRunnable() {
}
@Override
public void run() {
// super.run();
System.out.println("當前線程名稱" + Thread.currentThread().getName() + " = " + count);
count--;
}
}
第一次結果:
第二次結果:
第三次結果:
3.線程不安全
經過上面的例子隱身出了一個問題,就是線程安全問題,在實際場景當中,這是一個非常危險的問題,例如在雙11,秒殺,活動中,很多買家同時在0點的時候按購買,但貨品只有1個,很明顯這裏就是多線程處理同一個數據(貨品庫存量),那如果在線程不安全的情況下,會出現更上面例子一樣的情況,兩個人同一時間都在對同一個數字進行處理,結果有可能是,多名買家同時獲得這個商品。
爲什麼這樣呢,主要原因是count--這個代碼,一般情況,這行代碼做了3個動作
- 獲取count當前的值
- 對count的值進行-1的動作
- 對count重新賦值
那問題很明顯就出在第一步,假如A線程運行到這一行代碼獲取到count的值爲5,接下來,B線程搶到CPU的使用前,他也執行到了這行代碼,獲取count的值也是5,因爲A線程還沒有進行-1的操作
4.線程安全
那怎麼辦呢,關鍵字synchronized,在run方法前加上這句的代碼就可以達到排隊執行方法的作用了,意思就是說,在執行run代碼的時候,線程先查看當前代碼塊有沒有鑰匙,如果有鑰匙,即可進入這扇門(代碼塊),然後執行裏面的內容,執行完之後就會把鑰匙交出來,由下一個搶到鑰匙的人進入,並執行裏面的內容。在這個搶鑰匙的過程中是人人平等,誰先搶到鑰匙,誰先進入。我們叫這塊區域“互斥區”。
package com.xu.chap2;
public class TestThread2 {
public static void main(String[] args) {
//新建一個帶有Runnable接口的類
MyRunnable myRunnable = new MyRunnable();
//新建5個線程,但使用了同一個Runnable實例對象,意味着裏面的數據是共享的
//這裏的Thread(Runnable,String)是一個構造函數,第一次參數爲Runnable接口,第二個爲線程名稱
Thread thread_A = new Thread(myRunnable, "A");
Thread thread_B = new Thread(myRunnable, "B");
Thread thread_C = new Thread(myRunnable, "C");
Thread thread_D = new Thread(myRunnable, "D");
Thread thread_E = new Thread(myRunnable, "E");
thread_A.start();
thread_B.start();
thread_C.start();
thread_D.start();
thread_E.start();
}
}
class MyRunnable implements Runnable {
private int count = 5;
public MyRunnable() {
}
@Override
synchronized public void run() {
// super.run();
System.out.println("當前線程名稱" + Thread.currentThread().getName() + " = " + count);
count--;
}
}
第一次運行結果:
第二次運行結果:
至此,我們就成功實現了線程數據安全了,我們下回再見