先看一個多線程間共享數據的問題:
設計四個線程,其中兩個線程每次對data增加1,另外兩個線程每次對data減少1。
從問題來看,很明顯涉及到了線程間通數據的共享,四個線程共享一個 data,共同操作一個 data。我們先把上面這個問題放在一邊,慢慢分析多個線程之間共享數據的一些情況,從最簡單開始,分類分析完了后,到時候也好解決上面這個問題了。
1. 每個線程執行的任務相同
這是最簡單的一種情況,比如賣票,幾個線程共同操作記錄票數的那個變量,任務都是使它減一。針對這種情況,我們只需要寫一個類實現 Runnable 接口即可,在 run() 方法中對這個票進行減一,然后將這個 Runnable 扔給多個線程去執行,自然它們就操作同一個data了。看一下代碼:
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData task = new ShareData(); //一個類實現了Runnable接口
for(int i = 0; i < 4; i ++) { //四個線程來賣票
new Thread(task).start();
}
}
}
class ShareData implements Runnable {
private int data = 100;
@Override
public void run() { //賣票,每次一個線程進來,先判斷票數是否大於0
// while(data > 0) {
synchronized(this) {
if(data > 0) {
System.out.println(Thread.currentThread().getName() + ": " + data);
data--;
}
}
// }
}
}
這很好理解,也很容易實現,四個線程賣了4張票。運行結果為:
Thread-0: 100
Thread-3: 99
Thread-2: 98
Thread-1: 97
2. 每個線程執行不同的任務
就如上面那個題目所描述的,兩個線程執行 data 增,兩個線程執行 data 減。針對這種情況,我們要實現兩個 Runnable 了,因為很明顯有兩個不同的任務了,一個任務執行 data 增,另一個任務執行 data 減。為了便於維護,可以將兩個任務方法放到一個類中,然后將 data 也放在這個類中,然后傳到不同的 Runnabl e中,即可完成數據的共享。如下:
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData task = new ShareData(); //公共數據和任務放在task中
for(int i = 0; i < 2; i ++) { //開啟兩個線程增加data
new Thread(new Runnable() {
@Override
public void run() {
task.increment();
}
}).start();
}
for(int i = 0; i < 2; i ++) { //開啟兩個線程減少data
new Thread(new Runnable() {
@Override
public void run() {
task.decrement();
}
}).start();
}
}
}
class ShareData /*implements Runnable*/ {
private int data = 0;
public synchronized void increment() { //增加data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data++;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
public synchronized void decrement() { //減少data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data--;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
}
看一下打印結果:
Thread-0: before : 0
Thread-0: after : 1
Thread-1: before : 1
Thread-1: after : 2
Thread-2: before : 2
Thread-2: after : 1
Thread-3: before : 1
Thread-3: after : 0
這樣寫的好處是兩個任務方法可以直接在方法名上進行同步操作,這種模式的好處在前面的博文中已經有說過了,封裝的好。
最后總結一下,多個線程之間共享數據主要關注兩點就行:一是什么任務?幾個任務?二是幾個線程?記住 一點:幾個任務和幾個線程是沒有關系的!100個線程可以執行一個任務,也可以執行2個任務,3個任務……
如果只有一個任務,那說明多個線程執行一個任務,我們只要實現一個 Runnable 接口,把公共 data 放進 Runnable,把任務放進去 run() 中即可(任務注意要同步),然后開啟N個線程去執行這個任務即可;如果有M個任務,那我們新建一個專門執行任務的類,把公共的 data 放進類中,把任務作為類中的同步方法即可,然后開啟N個線程,每個線程中扔一個 Runnable,按照要求執行任務類中的方法即可。
到這里,讀者應該能體會到任務和線程的分離了,這種思想也算是面向對象的一種吧,思路很清晰。
多個線程之間共享數據就總結這么多,如有問題,歡迎交流,我們共同進步~
