之前都是業(yè)務層次開發(fā),現(xiàn)在公司進行的網(wǎng)絡編程,,一下子要了解太多java底層的東西并進行應用,,我現(xiàn)在邊學習邊應用。由于知識能力有限,,在上次發(fā)博客時出現(xiàn)了一個小小的紕漏,,而這個紕漏被細心的博友發(fā)現(xiàn)了。
首先感謝你的關注,,其次非常感謝你的建議和批評,。其實上次博客中說道要線程安全的取得緩沖變量確實有安全取得某變量的意思,,不過那個例子只是一個講解Socket應用的小示例,。如果真的要保證變量安全,使用靜態(tài)變量,,這好像有點不正常了,。
其實這一下子就圍繞在了一個話題上面,那就是變量的線程安全性?,F(xiàn)在就一個個來說,。
首先要肯定的是除了ThreadLocal和局部變量安全以外,靜態(tài)和實例變量都是不安全的,。
首先來看靜態(tài)變量:
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 {
- // 靜態(tài)變量
- private static int local = 0;
- @SuppressWarnings("unchecked")
- public void run() {
- // 靜態(tài)變量
- for (int i = 0; i <= 10; i++) {
- local += 1;
- try {
- Thread.sleep(500);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + local);
- }
- }
- }
運行后看控制臺輸出,,很容就發(fā)現(xiàn)有時候某線程使用變量時已經(jīng)被另一個線程修改了。
因為靜態(tài)變量是 靜態(tài)存儲方式,,所謂靜態(tài)存儲方式是指在程序運行期間分配固定的存儲空間的方式,。也就是說不管多少線程,訪問都是一個變量,,安全問題顯而易見,。
再說說實例變量:
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 {
- // 實例變量
- 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);
- }
- }
- }
也許你覺得這會安全,但是運行后安全問題你會馬上發(fā)現(xiàn),。
實例變量為對象實例私有,,在java虛擬機的堆中分配,如果在系統(tǒng)中只存在一個此對象的實例,,在多線程環(huán)境下,,就像靜態(tài)變量那樣,,被某個線程修改后,其他線程對修改均可見,,故線程非安全,;如果每個線程執(zhí)行都是在不同的對象中,那對象與對象之間的實例變量的修改將互不影響,,所以線程安全,。 而上面我們雖然是兩個線程,但是對象卻是一個,,所以不是你想想中的安全了,。
局部變量:
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);
- }
- }
- }
不行你就多運行幾遍,沒事的,,線程安全,。
每個線程執(zhí)行時將會把局部變量放在各自棧幀的工作內(nèi)存中,線程間不共享,,所以沒有安全問題,。
一般多線程編程時最會想到的是ThreadLocal:
ThreadLocal使用場合主要解決多線程中數(shù)據(jù)數(shù)據(jù)因并發(fā)產(chǎn)生不一致問題。ThreadLocal為每個線程的中并發(fā)訪問的數(shù)據(jù)提供一個副本,,通過訪問副本來運行業(yè)務,,這樣的結果是耗費了內(nèi)存,,單大大減少了線程同步所帶來性能消耗,,也減少了線程并發(fā)控制的復雜度,。
該類提供了線程局部 (thread-local) 變量,。這些變量不同于它們的普通對應物,,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,,它獨立于變量的初始化副本,。ThreadLocal 實例通常是類中的 private static 字段,,它們希望將狀態(tài)與某一個線程(例如,,用戶 ID 或事務 ID)相關聯(lián),。
Java代碼
- 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的區(qū)別,,雖然都是為了線程安全,但是卻又本質的區(qū)別,。
synchronized是利用鎖的機制,,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,,使得每個線程在某一時間訪問到的并不是同一個對象,,這樣就隔離了多個線程對數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,,它用于在多個線程間通信時能夠獲得數(shù)據(jù)共享,。Synchronized用于線程間的數(shù)據(jù)共享,,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。當然ThreadLocal并不能替代synchronized,它們處理不同的問題域,。synchronized是用來處理多線程環(huán)境下的數(shù)據(jù)同步,而ThreadLocal只是為了保存當前線程私有的某種狀態(tài),。
請您到ITEYE看我的原創(chuàng):http://cuisuqiang.
或支持我的個人博客,地址:http://www.
|