Java多執行緒之多個執行緒訪問共享物件和資料的方式
阿新 • • 發佈:2019-02-04
1.如果每個執行緒執行的程式碼相同,可以使用同一個Runable物件,這個Runable物件中有那個共享資料,例如賣票系統就可以這樣做。
package javaplay.test; public class MulteThreadShareData { public static void main(String[] args) { ShareData shareData = new ShareData(); new Thread(shareData).start(); new Thread(shareData).start(); } static class ShareData implements Runnable { int count = 100; @Override public void run() { while (count > 0) { decrease(); } } public synchronized void decrease() { count--; System.out.println(Thread.currentThread().getName() + "this count: " + count); } } }
這個示例是錯誤的,對共享變數的訪問必須必須加鎖!volatile只能保證可見性,不能保證原子性!
2.如果每個執行緒執行的程式碼不相同,這個時候需要用到不同的Runable物件,有如下兩種方式實現多個Runable物件中的資料共享。
2.1 將共享資料封裝在另外一個物件中,然後將這個物件逐一傳遞給各個Runnable物件。每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,這樣容易實現針對該資料進行的各個操作的互斥和通訊。
package javaplay.test; public class MulteThreadShareData { public static void main(String[] args) { final ShareData shareData = new ShareData(); new Thread(new Decrease(shareData)).start(); new Thread(new Increment(shareData)).start(); } static class Decrease implements Runnable { private ShareData shareData; public Decrease(ShareData shareData) { this.shareData = shareData; } @Override public void run() { shareData.decrease(); } } static class Increment implements Runnable { private ShareData shareData; public Increment(ShareData shareData) { this.shareData = shareData; } @Override public void run() { shareData.increment(); } } static class ShareData { int count = 100; public synchronized void decrease() { count--; System.out.println(Thread.currentThread().getName() + "decrease this count: " + count); } public synchronized void increment() { count++; System.out.println(Thread.currentThread().getName() + "increment this count: " + count); } } }
2.2 將這些Runnable物件作為某一個類中的內部類,共享資料作為這個外部類中的成員變數,每個執行緒對共享資料的操作方法也分配給外部類,以便實現對共享資料進行的各個操作的互斥和通訊,作為內部類的各個Runnable物件呼叫外部類的這些方法。
package javaplay.test; public class MulteThreadShareData { static int count = 100; public static void main(String[] args) { new Thread(new Decrease()).start(); new Thread(new Increment()).start(); } public synchronized static void decrease() { count--; System.out.println(Thread.currentThread().getName() + "decrease this count: " + count); } public synchronized static void increment() { count++; System.out.println(Thread.currentThread().getName() + "increment this count: " + count); } static class Decrease implements Runnable { @Override public void run() { decrease(); } } static class Increment implements Runnable { @Override public void run() { increment(); } } }
2.3 上面兩種方式的組合:將共享資料封裝在另外一個物件中,每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,物件作為這個外部類中的成員變數或方法中的區域性變數,每個執行緒的Runnable物件作為外部類中的成員內部類或區域性內部類。
package javaplay.test;
public class MulteThreadShareData {
public static void main(String[] args) {
final ShareData shareData = new ShareData();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
shareData.decrease();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
shareData.increment();
}
}
}).start();
}
static class ShareData {
int count = 100;
public synchronized void decrease() {
count--;
System.out.println(Thread.currentThread().getName() + "this count: " + count);
}
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + "this count: " + count);
}
}
}
2.4 總之,要同步互斥的幾段程式碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊。