Java如何實現(xiàn)多個線程之間共享數(shù)據(jù)
實現(xiàn)多個線程之間共享數(shù)據(jù)
一、 如果每個線程執(zhí)行的代碼相同
可以使用同一個Runnable對象,這個Runnable對象中有那個共享數(shù)據(jù),例如:賣票系統(tǒng)
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不然執(zhí)行每個線程都會占用完畢 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } class TicketDemo { public static void main(String[] args) { //只建立了一個Ticket對象,內(nèi)存中只有一個tick成員變量,所以是共享數(shù)據(jù) 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
二、 如果每個線程執(zhí)行的代碼不同
1、具體實現(xiàn)
將共享數(shù)據(jù)封裝在另外一個對象中,然后將這個對象逐一傳遞給各個Runnable對象。每個線程對共享數(shù)據(jù)的操作方法也分配到那個對象身上去完成,這樣容易實現(xiàn)針對該數(shù)據(jù)進行的各個操作的互斥和通信。
思想: 一個類提供數(shù)據(jù)和操作數(shù)據(jù)的同步方法,另外定義兩個線程通過構造函數(shù)接收并操作數(shù)據(jù),在主函數(shù)中直接創(chuàng)建線程對象,即可完成操作(可以實現(xiàn)兩個內(nèi)部類,不用構造方法傳值,使用final定義data局部變量)
例如: 設計4個線程,其中兩個線程每次對j增加1,另外兩個線程每次對j減少1
public class MultyThreadShareMethod1 { public static void main(String[] args){ //將數(shù)據(jù)封裝到一個對象上, ShareData2 data1 = new ShareData2(); //在runnable的構造函數(shù)中直接傳入去操作 for(int i=0;i<2;i++){ new Thread(new MyRunnable1(data1)).start(); new Thread(new MyRunnable2(data1)).start(); } } } //封裝共享數(shù)據(jù)和操作共享數(shù)據(jù)方法的類 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); } } //增加的線程,需要傳入一個共享數(shù)據(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(); } } } //減少的線程,需要傳入一個共享數(shù)據(jù) 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、 技巧總結
要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現(xiàn)它們之間的同步互斥或通信。
極端且簡單的方式,即在任意一個類中定義一個static的變量,這將被所有線程共享。
多線程之間共享數(shù)據(jù)的方式探討
方式一:代碼一致
如果每個線程執(zhí)行的代碼相同,可以用一個 Runnable 對象,這個 Runnable 對象中存放那個共享數(shù)據(jù)(賣票系統(tǒng)可以這樣做)。
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 { // 共享的數(shù)據(jù) 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); } } } } }
方式二:代碼不一致
如果每個線程執(zhí)行的代碼不同時,就需要不同的 Runnable 對象:
a. 將共享數(shù)據(jù)封裝在一個對象中,然后將這個對象逐一傳遞給各個 Runnable 對象,每個線程對共享數(shù)據(jù)操作的方法也分配到這個對象中,這樣容易實現(xiàn)針對該數(shù)據(jù)進行的各個操作的互斥通信。
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++){ //對數(shù)據(jù)進行增加 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++){ //對數(shù)據(jù)進行減少 this.data.decrement(); } } } /** 將共享的數(shù)據(jù)封裝成一個類 */ class ShareData{ //共享數(shù)據(jù) 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 對象作為某一個類的內(nèi)部類,共享數(shù)據(jù)作為這個外部類的成員變量,每個線程對共享數(shù)據(jù)的操作方法也分配到外部類中,實現(xiàn)共享數(shù)據(jù)的互斥和通信操作,作為內(nèi)部類的各個 Runnable 對象調(diào)用外部類的這些方法。
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); } /** * 作為內(nèi)部類的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. 以上兩種方法的組合:將共享數(shù)據(jù)封裝到一個對象中,每個線程對共享數(shù)據(jù)的操作方法也分配到對象中,對象作為外部類的成員變量或方法中的局部變量,每個線程的 Runnable 作為成員內(nèi)部類或局部內(nèi)部類。
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(); } } /** 封裝共享數(shù)據(jù)的對象 */ class ShareData{ //共享數(shù)據(jù) private int j=0; /** 對共享數(shù)據(jù)進行操作的方法 */ 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; } }
總之,要同步互斥的幾段代碼最好放在幾個獨立的方法中,這些方法再放入一個類中,這樣比較容易實現(xiàn)它們之間的同步互斥和通信。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring如何消除代碼中的if-else/switch-case
這篇文章主要給大家介紹了關于Spring如何消除代碼中if-else/switch-case的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-04-04Spring+SpringMVC配置事務管理無效原因及解決辦法詳解
這篇文章主要介紹了Spring+SpringMVC配置事務管理無效原因及解決辦法詳解,具有一定借鑒價值,需要的朋友可以參考下2017-12-12SSH框架網(wǎng)上商城項目第7戰(zhàn)之整合Struts2和Json
SSH框架網(wǎng)上商城項目第7戰(zhàn)之整合Struts2和Json,打通EasyUI和Struts2之間的交互,感興趣的小伙伴們可以參考一下2016-05-05springboot+mybatis-plus實現(xiàn)內(nèi)置的CRUD使用詳解
這篇文章主要介紹了springboot+mybatis-plus實現(xiàn)內(nèi)置的CRUD使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12