ThreadLocal,靜態變量,實例變量,局部變量的線程安全,回復:ByteBuffer 到底怎么用?網絡編程中一點總結!
之前都是業務層次開發,現在公司進行的網絡編程,一下子要了解太多java底層的東西並進行應用,我現在邊學習邊應用。由於知識能力有限,在上次發博客時出現了一個小小的紕漏,而這個紕漏被細心的博友發現了。
首先感謝你的關注,其次非常感謝你的建議和批評。其實上次博客中說道要線程安全的取得緩沖變量確實有安全取得某變量的意思,不過那個例子只是一個講解Socket應用的小示例。如果真的要保證變量安全,使用靜態變量,這好像有點不正常了。
其實這一下子就圍繞在了一個話題上面,那就是變量的線程安全性。現在就一個個來說。
首先要肯定的是除了ThreadLocal和局部變量安全以外,靜態和實例變量都是不安全的。
首先來看靜態變量:
- package com;
- /**
- * @說明 變量安全測試
- * @author 崔素強
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- // 靜態變量
- private static int local = 0;
- @SuppressWarnings("unchecked")
- public void run() {
- // 靜態變量
- for (int i = 0; i <= 10; i++) {
- local += 1;
- try {
- Thread.sleep(500);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + local);
- }
- }
- }
運行后看控制台輸出,很容就發現有時候某線程使用變量時已經被另一個線程修改了。
因為靜態變量是 靜態存儲方式,所謂靜態存儲方式是指在程序運行期間分配固定的存儲空間的方式。也就是說不管多少線程,訪問都是一個變量,安全問題顯而易見。
再說說實例變量:
- package com;
- /**
- * @說明 變量安全測試
- * @author 崔素強
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- // 實例變量
- int locals = 0;
- @SuppressWarnings("unchecked")
- public void run() {
- for (int i = 0; i <= 10; i++) {
- locals += 1;
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + locals);
- }
- }
- }
也許你覺得這會安全,但是運行后安全問題你會馬上發現。
實例變量為對象實例私有,在java虛擬機的堆中分配,如果在系統中只存在一個此對象的實例,在多線程環境下,就像靜態變量那樣,被某個線程修改后,其他線程對修改均可見,故線程非安全;如果每個線程執行都是在不同的對象中,那對象與對象之間的實例變量的修改將互不影響,所以線程安全。 而上面我們雖然是兩個線程,但是對象卻是一個,所以不是你想想中的安全了。
局部變量:
- package com;
- /**
- * @說明 變量安全測試
- * @author 崔素強
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- @SuppressWarnings("unchecked")
- public void run() {
- // 局部變量
- int locals = 0;
- for (int i = 0; i <= 5; i++) {
- locals += 1;
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + locals);
- }
- }
- }
不行你就多運行幾遍,沒事的,線程安全。
每個線程執行時將會把局部變量放在各自棧幀的工作內存中,線程間不共享,所以沒有安全問題。
一般多線程編程時最會想到的是ThreadLocal:
ThreadLocal使用場合主要解決多線程中數據數據因並發產生不一致問題。ThreadLocal為每個線程的中並發訪問的數據提供一個副本,通過訪問副本來運行業務,這樣的結果是耗費了內存,單大大減少了線程同步所帶來性能消耗,也減少了線程並發控制的復雜度。
該類提供了線程局部 (thread-local) 變量。這些變量不同於它們的普通對應物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。
- package com;
- /**
- * @說明 變量安全測試
- * @author 崔素強
- */
- public class ThreadLocalTest {
- // 線程安全變量
- @SuppressWarnings("unchecked")
- public static ThreadLocal threadLocal = new ThreadLocal();
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- @SuppressWarnings("unchecked")
- public void run() {
- // 測試線程安全
- ThreadLocal threadLocal = ThreadLocalTest.threadLocal;
- for (int i = 1; i <= 10; i++) {
- if (threadLocal.get() == null)
- threadLocal.set(new Integer(0));
- int x = ((Integer) threadLocal.get()).intValue();
- x += 1;
- threadLocal.set(new Integer(x));
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + ((Integer) threadLocal.get()).intValue());
- }
- }
- }
上面的代碼其實每個線程都會有自己的變量副本,所以也不會有安全問題的。
至於它和synchronized的區別,雖然都是為了線程安全,但是卻又本質的區別。
synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的並不是同一個對象,這樣就隔離了多個線程對數據的數據共享。而Synchronized卻正好相反,它用於在多個線程間通信時能夠獲得數據共享。Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。當然ThreadLocal並不能替代synchronized,它們處理不同的問題域。synchronized是用來處理多線程環境下的數據同步,而ThreadLocal只是為了保存當前線程私有的某種狀態。