Java如何實現多個線程之間共享數據
實現多個線程之間共享數據
一、 如果每個線程執行的代碼相同
可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如:賣票系統
class Ticket implements Runnable{ private int tick = 20; Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ if(tick>0){ //隻能try,因為run是復寫瞭Runnable接口的run,接口的run沒有拋 try{Thread.sleep(100);}catch(Exception e){} //使用sleep不然執行每個線程都會占用完畢 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } class TicketDemo { public static void main(String[] args) { //隻建立瞭一個Ticket對象,內存中隻有一個tick成員變量,所以是共享數據 Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } }
輸出結果
Thread-0….sale : 20
Thread-1….sale : 19
…….
Thread-3….sale : 2
Thread-3….sale : 1
二、 如果每個線程執行的代碼不同
1、具體實現
將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個Runnable對象。每個線程對共享數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。
思想: 一個類提供數據和操作數據的同步方法,另外定義兩個線程通過構造函數接收並操作數據,在主函數中直接創建線程對象,即可完成操作(可以實現兩個內部類,不用構造方法傳值,使用final定義data局部變量)
例如: 設計4個線程,其中兩個線程每次對j增加1,另外兩個線程每次對j減少1
public class MultyThreadShareMethod1 { public static void main(String[] args){ //將數據封裝到一個對象上, ShareData2 data1 = new ShareData2(); //在runnable的構造函數中直接傳入去操作 for(int i=0;i<2;i++){ new Thread(new MyRunnable1(data1)).start(); new Thread(new MyRunnable2(data1)).start(); } } } //封裝共享數據和操作共享數據方法的類 class ShareData2{ private int j = 10; public synchronized void increment() { j++; System.out.println(Thread.currentThread().getName()+" inc : "+j); } public synchronized void decrement() { j--; System.out.println(Thread.currentThread().getName()+" dec : "+j); } } //增加的線程,需要傳入一個共享數據 class MyRunnable1 implements Runnable { private ShareData2 data; public MyRunnable1(ShareData2 data) { this.data = data; } @Override public void run() { for(int i=0;i<10;i++){ data.increment(); } } } //減少的線程,需要傳入一個共享數據 class MyRunnable2 implements Runnable { private ShareData2 data; public MyRunnable2(ShareData2 data) { this.data = data; } @Override public void run() { for(int i=0;i<10;i++){ data.decrement(); } } }
輸出結果
Thread-0 inc : 11
…
Thread-1 dec : 10
2、 技巧總結
要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥或通信。
極端且簡單的方式,即在任意一個類中定義一個static的變量,這將被所有線程共享。
多線程之間共享數據的方式探討
方式一:代碼一致
如果每個線程執行的代碼相同,可以用一個 Runnable 對象,這個 Runnable 對象中存放那個共享數據(賣票系統可以這樣做)。
public class MultiThreadShareData { public static void main(String[] args) { MyShareData shareData=new MyShareData(); //放入不同的線程中 new Thread(shareData).start(); new Thread(shareData).start(); } } class MyShareData implements Runnable { // 共享的數據 private int count = 100; @Override public void run() { while (count > 0) { synchronized (this) { if (count > 0) { count--; System.out.println(Thread.currentThread().getName() + " 減瞭1,count還剩:" + count); } } } } }
方式二:代碼不一致
如果每個線程執行的代碼不同時,就需要不同的 Runnable 對象:
a. 將共享數據封裝在一個對象中,然後將這個對象逐一傳遞給各個 Runnable 對象,每個線程對共享數據操作的方法也分配到這個對象中,這樣容易實現針對該數據進行的各個操作的互斥通信。
public class MultiThreadShareData { private int shareData=0; public static void main(String[] args) { ShareData data = new ShareData(); new Thread(new MyRunnable1(data)).start(); new Thread(new MyRunnable2(data)).start(); } } class MyRunnable1 implements Runnable{ private ShareData data; public MyRunnable1(ShareData data){ this.data=data; } @Override public void run() { for(int i=0;i<100;i++){ //對數據進行增加 this.data.increment(); } } } class MyRunnable2 implements Runnable{ private ShareData data; public MyRunnable2(ShareData data){ this.data=data; } @Override public void run() { for(int i=0;i<100;i++){ //對數據進行減少 this.data.decrement(); } } } /** 將共享的數據封裝成一個類 */ class ShareData{ //共享數據 private int j=0; public synchronized void increment(){ this.j++; System.out.println(Thread.currentThread().getName()+":j增加瞭1後j="+j); } public synchronized void decrement() { this.j--; System.out.println(Thread.currentThread().getName()+":j減少瞭1後j="+j); } public int getJ() { return j; } }
b. 將 Runnable 對象作為某一個類的內部類,共享數據作為這個外部類的成員變量,每個線程對共享數據的操作方法也分配到外部類中,實現共享數據的互斥和通信操作,作為內部類的各個 Runnable 對象調用外部類的這些方法。
public class MultiThreadShareData { private int shareData=0; public static void main(String[] args) { MultiThreadShareData m=new MultiThreadShareData(); //初始化Runnable對象 MyRunnable1 myRunnable1 = m.new MyRunnable1(); MyRunnable2 myRunnable2=m.new MyRunnable2(); //開啟線程 new Thread(myRunnable1).start(); new Thread(myRunnable2).start(); } private synchronized void increment(){ this.shareData++; System.out.println(Thread.currentThread().getName()+":shareData增加瞭1後shareData="+shareData); } private synchronized void decrement() { this.shareData--; System.out.println(Thread.currentThread().getName()+":shareData減少瞭1後shareData="+shareData); } /** * 作為內部類的Runnable對象 */ class MyRunnable1 implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ increment(); } } } class MyRunnable2 implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ decrement(); } } } }
c. 以上兩種方法的組合:將共享數據封裝到一個對象中,每個線程對共享數據的操作方法也分配到對象中,對象作為外部類的成員變量或方法中的局部變量,每個線程的 Runnable 作為成員內部類或局部內部類。
public class MultiThreadShareData { public static void main(String[] args) { ShareData data = new ShareData(); new Thread(()->{ for(int i=0;i<100;i++){ data.increment(); } }).start(); new Thread(()->{ for (int j=0;j<100;j++) { data.decrement(); } }).start(); } } /** 封裝共享數據的對象 */ class ShareData{ //共享數據 private int j=0; /** 對共享數據進行操作的方法 */ public synchronized void increment(){ this.j++; System.out.println(Thread.currentThread().getName()+":j增加瞭1後j="+j); } public synchronized void decrement() { this.j--; System.out.println(Thread.currentThread().getName()+":j減少瞭1後j="+j); } public int getJ() { return j; } }
總之,要同步互斥的幾段代碼最好放在幾個獨立的方法中,這些方法再放入一個類中,這樣比較容易實現它們之間的同步互斥和通信。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。