【并發技術06】線程范圍內共享數據
假設現在有個公共的變量 data,有不同的線程都可以去操作它,如果在不同的線程對 data 操作完成后再去取這個 data,那么肯定會出現線程間的數據混亂問題,因為 A 線程在取 data 數據前可能 B 線程又對其進行了修改,下面寫個程序來說明一下該問題:
public?class?ThreadScopeShareData?{????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()?{????????????????????int?temp?=?new?Random().nextInt();????????????????????System.out.println(Thread.currentThread().getName()?+?"?has?put?a?data:?"?+?temp);?//打印出來為了看效果????????????????????data?=?temp;?//操作數據:賦新值????????????????????new?TestA().getData();????????????????????new?TestB().getData();????????????????}????????????}).start();????????}????}????static?class?TestA?{????????public?void?getData()?{????????????System.out.println("A?get?data?from?"?+?Thread.currentThread().getName()?+?":?"?+?data);//取出公共數據data????????}????}????static?class?TestB?{????????public?void?getData()?{????????????System.out.println("B?get?data?from?"?+?Thread.currentThread().getName()?+?":?"?+?data);????????}????}}
我們來看一下打印出來的結果:
Thread-0 has put a data: -1885917900
Thread-1 has put a data: -1743455464
A get data from Thread-0: -1743455464
A get data from Thread-1: -1743455464
B get data from Thread-1: -1743455464
B get data from Thread-0: -1743455464
從結果中可以看出,兩次對 data 賦的值確實不一樣,但是兩個線程最后打印出來的都是最后賦的那個值,說明 Thread-0 拿出的數據已經不對了,這就是線程間共享數據帶來的問題。
當然,我們完全可以使用 synchronized 關鍵字將?run()?方法中的幾行代碼給套起來,這樣每個線程各自執行完,打印出各自的信息,這是沒問題的,確實可以解決上面的線程間共享數據問題。但是,這是以其他線程被阻塞為代價的,即 Thread-0 在執行的時候,Thread-1 就被阻塞了,必須等待 Thread-0 執行完了才能執行。
那么如果我想兩個線程同時跑,并且互不影響各自取出的值,該怎么辦呢?這也是本文所要總結的重點,解決該問題的思想是:雖然現在都在操作公共數據 data,但是不同的線程本身對這個 data 要維護一個副本,這個副本不是線程間所共享的,而是每個線程所獨有的,所以不同線程中所維護的 data 是不一樣的,最后取的時候,是哪個線程,我就從哪個線程中取該 data。
基于上面這個思路,我們再把上面的程序做一修改,如下:
public?class?ThreadScopeShareData?{????private?static?int?data?=?0;//公共的數據????//定義一個Map以鍵值對的方式存儲每個線程和它對應的數據,即Thread:data????private?static?Map
上面程序中維護了一個 Map,鍵值對分別是線程和它的數據,那么在操作 data 的時候,先把各自的數據保存到這個 Map 中,這樣每個線程保存的肯定不同,當再取的時候,根據當前線程對象作為 key 來取出對應的 data 副本,這樣不同的線程之間就不會相互影響了。這個 HashMap 也需要包裝一下,因為 HashMap 是非線程安全的,上面的程序中,不同的線程有對 HashMap 進行寫操作,就有可能產生并發問題,所以也要包裝一下。最后來看一下執行結果:
Thread-0 has put a data: 1817494992
Thread-1 has put a data: -1189758355
A get data from Thread-0: 1817494992
A get data from Thread-1: -1189758355
B get data from Thread-0: 1817494992
B get data from Thread-1: -1189758355
就是線程范圍內共享數據,即同一個線程里面這個數據是共享的,線程間是不共享的。
這讓我聯想到了學習數據庫的時候用到的 ThreadLocal,操作數據庫需要 connection,如果當前線程中有就拿當前線程中存的 connection,否則就新建一個放到當前線程中,這樣就不會出現問題,因為每個線程本身共享了一個 connection,它不是線程間共享的。這也很好理解,這個 connection 肯定不能共享,假設 A 和 B 用戶都拿到這個 connection 并開啟了事務,現在 A 開始轉賬了,但是錢還沒轉好,B 轉好了關閉了事務,那么 A 那邊就出問題了。線程范圍內共享數據的問題就總結這么多吧。
HashMap 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。