Java多線程2 多個線程之間共享數據

Java多線程目錄

線程范圍的共享變量

多個業務模塊針對同一個static變量的操作 要保證在不同線程中 各模塊操作的是自身對應的變量對象

public class ThreadScopeSharaData {

    private static  int data = 0 ;

    public static void main(String[] args) {
        for(int i = 0 ;i<2 ;i++){
            new Thread(new Runnable(){

                @Override
                public void run() {
                    data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                    new A().get() ;
                    new B().get() ;
                }

            }).start() ;
        }

    }

    static class A {
        public int get(){
            System.out.println("A from " + Thread.currentThread().getName()
                    + " get data :" + data);
            return data ;
        }
    }

    static class B{
        public int get(){
            System.out.println("B from " + Thread.currentThread().getName()
                    + " get data :" + data);
            return data ;
        }
    }
}

模塊A ,B都需要訪問static的變量data 在線程0中會隨機生成一個data值 假設為10 那么此時模塊A和模塊B在線程0中得到的data的值為10 ;在線程1中 假設會為data賦值為20 那么在當前線程下

模塊A和模塊B得到data的值應該為20

看程序執行的結果:

Thread-0 put random data:-2009009251
Thread-1 put random data:-2009009251
A from Thread-0 get data :-2009009251
A from Thread-1 get data :-2009009251
B from Thread-0 get data :-2009009251
B from Thread-1 get data :-2009009251
Thread-0 put random data:-2045829602
Thread-1 put random data:-1842611697
A from Thread-0 get data :-1842611697
A from Thread-1 get data :-1842611697
B from Thread-0 get data :-1842611697
B from Thread-1 get data :-1842611697

會出現兩種情況
1.由于線程執行速度,新的隨機值將就的隨機值覆蓋 data 值一樣
2.data 值不一樣,但 A、B線程都

1.使用Map實現線程范圍內數據的共享

可是將data數據和當前允許的線程綁定在一塊,在模塊A和模塊B去獲取數據data的時候 是通過當前所屬的線程去取得data的結果就行了。
聲明一個Map集合 集合的Key為Thread 存儲當前所屬線程 Value 保存data的值,代碼如下:

public class ThreadScopeSharaData {


    private static Map<Thread, Integer> threadData = new HashMap<>();

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadData.put(Thread.currentThread(), data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadData.get(Thread.currentThread());

            System.out.println("A from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }

    static class B {
        public void get() {
            int data = threadData.get(Thread.currentThread());
            System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }
}
Thread-0 put random data:-123490895
Thread-1 put random data:-1060992440
A from Thread-0 get data:-123490895
A from Thread-1 get data:-1060992440
B from Thread-0 get data:-123490895
B from Thread-1 get data:-1060992440
2.ThreadLocal實現線程范圍內數據的共享

(1)訂單處理包含一系列操作:減少庫存量、增加一條流水臺賬、修改總賬,這幾個操作要在同一個事務中完成,通常也即同一個線程中進行處理,如果累加公司應收款的操作失敗了,則應該把前面的操作回滾,否則,提交所有操作,這要求這些操作使用相同的數據庫連接對象,而這些操作的代碼分別位于不同的模塊類中。
(2)銀行轉賬包含一系列操作: 把轉出帳戶的余額減少,把轉入帳戶的余額增加,這兩個操作要在同一個事務中完成,它們必須使用相同的數據庫連接對象,轉入和轉出操作的代碼分別是兩個不同的帳戶對象的方法。
(3)例如Strut2的ActionContext,同一段代碼被不同的線程調用運行時,該代碼操作的數據是每個線程各自的狀態和數據,對于不同的線程來說,getContext方法拿到的對象都不相同,對同一個線程來說,不管調用getContext方法多少次和在哪個模塊中getContext方法,拿到的都是同一個。
4.實驗案例:定義一個全局共享的ThreadLocal變量,然后啟動多個線程向該ThreadLocal變量中存儲一個隨機值,接著各個線程調用另外其他多個類的方法,這多個類的方法中讀取這個ThreadLocal變量的值,就可以看到多個類在同一個線程中共享同一份數據。
5.實現對ThreadLocal變量的封裝,讓外界不要直接操作ThreadLocal變量。
(1)對基本類型的數據的封裝,這種應用相對很少見。
(2)對對象類型的數據的封裝,比較常見,即讓某個類針對不同線程分別創建一個獨立的實例對象。

public class ThreadLocalTest {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadLocal.set(data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadLocal.get();

            System.out.println("A from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }

    static class B {
        public void get() {
            int data = threadLocal.get();
            System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }
}
Thread-0 put random data:-2015900409
Thread-1 put random data:-645411160
A from Thread-0 get data:-2015900409
A from Thread-1 get data:-645411160
B from Thread-0 get data:-2015900409
B from Thread-1 get data:-645411160
優化
public class ThreadLocalTest {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    //private static ThreadLocal<MyThreadScopeData> myThreadScopeDataThreadLocal = new ThreadLocal<>();


    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadLocal.set(data);

//                    MyThreadScopeData myThreadScopeData = new MyThreadScopeData();
//                    myThreadScopeData.setName("name" + data);
//                    myThreadScopeData.setAge(data);
//                    myThreadScopeDataThreadLocal.set(myThreadScopeData);

                    //獲取與當前線程綁定的實例并設置值  
                    MyThreadScopeData.getThreadInstance().setName("name" + data);
                    MyThreadScopeData.getThreadInstance().setAge(data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadLocal.get();


//            MyThreadScopeData myData = myThreadScopeDataThreadLocal.get();
//
//
//            System.out.println("A from " + Thread.currentThread().getName()
//                    + " getMyData: " + myData.getName() + "," + myData.getAge());

            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("A from " + Thread.currentThread().getName()
                    + " getMyData: " + myData.getName() + "," + myData.getAge());
        }
    }

    static class B {
        public void get() {
            int data = threadLocal.get();
            //System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("B from " + Thread.currentThread().getName()
                    + " getMyData: " + myData.getName() + "," + myData.getAge());
        }
    }
}

//一個綁定當前線程的類
class MyThreadScopeData {

    private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<>();
    private String name;
    private int age;

    private MyThreadScopeData() {
    }

    //定義一個靜態方法,返回各線程自己的實例
    //這里不必用同步,因為每個線程都要創建自己的實例,所以沒有線程安全問題。
    public static MyThreadScopeData getThreadInstance() {
        //獲取當前線程綁定的實例
        MyThreadScopeData instance = map.get();
        if (instance == null) {
            instance = new MyThreadScopeData();
            map.set(instance);
        }
        return instance;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


}
Thread-1 put random data:-1041517189
Thread-0 put random data:-98835751
A from Thread-1 getMyData: name-1041517189,-1041517189
A from Thread-0 getMyData: name-98835751,-98835751
B from Thread-1 getMyData: name-1041517189,-1041517189
B from Thread-0 getMyData: name-98835751,-98835751
實例:

設計4個線程,其中兩個線程每次對j增加1,另外兩個線程對j每次減少1,寫出程序。
1、如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,賣票系統就可以這么做。

public class SellTicket {
    //賣票系統,多個窗口的處理邏輯是相同的
    public static void main(String[] args) {
        Ticket t = new Ticket();
        new Thread(t).start();
        new Thread(t).start();
    }
}

/**
 * 將屬性和處理邏輯,封裝在一個類中
 *
 * @author yang
 */
class Ticket implements Runnable {

    private int ticket = 10;

    public synchronized void run() {
        while (ticket > 0) {
            ticket--;
            System.out.println("當前票數為:" + ticket);
        }
    }
}

2、如果每個線程執行的代碼不同,這時候需要用不同的Runnable對象,例如,設計2個線程。一個線程對j增加1,另外一個線程對j減1,銀行存取款系統。

public class MultiThreadShareData {
    private int j;
    public static void main(String[] args) {
        MultiThreadShareData multiThreadShareData = new MultiThreadShareData();
        for(int i=0;i<2;i++){
            new Thread(multiThreadShareData.new ShareData1()).start();//增加
            new Thread(multiThreadShareData.new ShareData2()).start();//減少
        }
    }
    //自增
    private synchronized void Inc(){
        j++;
        System.out.println(Thread.currentThread().getName()+" inc "+j);
    }
    //自減
    private synchronized void Dec(){
        j--;
        System.out.println(Thread.currentThread().getName()+" dec "+j);
    }

    class ShareData1 implements Runnable {
        public void run() {
            for(int i=0;i<5;i++){
                Inc();
            }
        }
    }
    class ShareData2 implements Runnable {
        public void run() {
            for(int i=0;i<5;i++){
                Dec();
            }
        }
    }
}
Thread-0 inc 1
Thread-0 inc 2
Thread-0 inc 3
Thread-0 inc 4
Thread-0 inc 5
Thread-1 dec 4
Thread-1 dec 3
Thread-2 inc 4
Thread-2 inc 5
Thread-2 inc 6
Thread-2 inc 7
Thread-2 inc 8
Thread-1 dec 7
Thread-1 dec 6
Thread-1 dec 5
Thread-3 dec 4
Thread-3 dec 3
Thread-3 dec 2
Thread-3 dec 1
Thread-3 dec 0
特別感謝:

JAVA 并發編程-多個線程之間共享數據
多線程:(五)多個線程之間共享數據

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容

  • 進程和線程 進程 所有運行中的任務通常對應一個進程,當一個程序進入內存運行時,即變成一個進程.進程是處于運行過程中...
    勝浩_ae28閱讀 5,131評論 0 23
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,330評論 11 349
  • 文/蕭讓 上午 我垛餡 姥姥和面 姥姥搟面皮兒 我包餃子 姥爺燒鍋 我和姥姥 姥爺說著話 水燒好了 下了餃子 我們...
    蕭讓聽雪閱讀 344評論 0 2
  • 關鍵詞:交互設計,人機交互,用戶體驗設計 在學習和生活中,我們經常會遇到三個名詞:“交互設計”,“人機交互”,“用...
    曉臣同學愛學習閱讀 13,685評論 2 37
  • 近幾年,創新和顛覆或許是出現頻率最高的兩個詞匯,尤其是在互聯網行業。然而,無論是智能電視也好,智能手機也罷,創新和...
    喻拓閱讀 583評論 0 49