# Java線程(一):線程安全與不安全
作為一個(gè)Java web開(kāi)發(fā)人員,很少也不需要去處理線程,因?yàn)榉?wù)器已經(jīng)幫我們處理好了。記得大一剛學(xué)Java的時(shí)候,老師帶著我們做了一個(gè)局域網(wǎng)聊天室,用到了AWT、Socket、多線程、I/O,編寫(xiě)的客戶(hù)端和服務(wù)器,當(dāng)時(shí)做出來(lái)很興奮,回學(xué)校給同學(xué)們演示,感覺(jué)自己好NB,呵呵,扯遠(yuǎn)了。上次在百度開(kāi)發(fā)者大會(huì)上看到一個(gè)提示語(yǔ),自己寫(xiě)的代碼,6個(gè)月不看也是別人的代碼,自己學(xué)的知識(shí)也同樣如此,學(xué)完的知識(shí)如果不使用或者不常?;仡櫍敲催€不是自己的知識(shí)。大學(xué)零零散散搞了不到四年的Java,我相信很多人都跟我一樣,JavaSE基礎(chǔ)沒(méi)打牢,就急忙忙、興沖沖的搞JavaEE了,然后學(xué)習(xí)一下前臺(tái)開(kāi)發(fā)(html、css、javascript),有可能還搞搞jquery、extjs,再然后是Struts、hibernate、spring,然后聽(tīng)說(shuō)找工作得會(huì)linux、oracle,又去學(xué),在這個(gè)過(guò)程中,是否迷失了,雖然學(xué)習(xí)面很廣,但就像《神雕俠侶》中黃藥師評(píng)價(jià)楊過(guò),博而不精、雜而不純,這一串下來(lái),感覺(jué)做Java開(kāi)發(fā)好難,并不是學(xué)著難,而是知識(shí)面太廣了,又要精通這個(gè),又要精通那個(gè),這只是我迷茫時(shí)候的想法,現(xiàn)在我已經(jīng)找到方向了。
回歸正題,當(dāng)我們查看JDK API的時(shí)候,總會(huì)發(fā)現(xiàn)一些類(lèi)說(shuō)明寫(xiě)著,線程安全或者線程不安全,比如說(shuō)StringBuilder中,有這么一句,“將`StringBuilder`?的實(shí)例用于多個(gè)線程是不安全的。如果需要這樣的同步,則建議使用`[StringBuffer](http://blog.csdn.net/ghsau/article/details/7421217 "java.lang 中的類(lèi)")`。 ”,那么下面手動(dòng)創(chuàng)建一個(gè)線程不安全的類(lèi),然后在多線程中使用這個(gè)類(lèi),看看有什么效果。
Count.java:
~~~
public?class?Count?{??
????private?int?num;??
????public?void?count()?{??
????????for(int?i?=?1;?i?10;?i++)?{??
????????????num?+=?i;??
????????}??
????????System.out.println(Thread.currentThread().getName()?+?"-"?+?num);??
????}??
}??
~~~
在這個(gè)類(lèi)中的count方法是計(jì)算1一直加到10的和,并輸出當(dāng)前線程名和總和,我們期望的是每個(gè)線程都會(huì)輸出55。
ThreadTest.java:
~~~
public?class?ThreadTest?{??
????public?static?void?main(String[]?args)?{??
????????Runnable?runnable?=?new?Runnable()?{??
????????????Count?count?=?new?Count();??
????????????public?void?run()?{??
????????????????count.count();??
????????????}??
????????};??
????????for(int?i?=?0;?i?10;?i++)?{??
????????????new?Thread(runnable).start();??
????????}??
????}??
}??
~~~
這里啟動(dòng)了10個(gè)線程,看一下輸出結(jié)果:
~~~
Thread-0-55??
Thread-1-110??
Thread-2-165??
Thread-4-220??
Thread-5-275??
Thread-6-330??
Thread-3-385??
Thread-7-440??
Thread-8-495??
Thread-9-550??
~~~
只有Thread-0線程輸出的結(jié)果是我們期望的,而輸出的是每次都累加的,這里累加的原因以后的博文會(huì)說(shuō)明,那么要想得到我們期望的結(jié)果,有幾種解決方案:
1\. 將Count中num變成count方法的局部變量;
~~~
public?class?Count?{??
????public?void?count()?{??
????????int?num?=?0;??
????????for(int?i?=?1;?i?10;?i++)?{??
????????????num?+=?i;??
????????}??
????????System.out.println(Thread.currentThread().getName()?+?"-"?+?num);??
????}??
}??
~~~
2\. 將線程類(lèi)成員變量拿到run方法中,這時(shí)count引用是線程內(nèi)的局部變量;
~~~
public?class?ThreadTest4?{??
????public?static?void?main(String[]?args)?{??
????????Runnable?runnable?=?new?Runnable()?{??
????????????public?void?run()?{??
????????????????Count?count?=?new?Count();??
????????????????count.count();??
????????????}??
????????};??
????????for(int?i?=?0;?i?10;?i++)?{??
????????????new?Thread(runnable).start();??
????????}??
????}??
}???
~~~
3\. 每次啟動(dòng)一個(gè)線程使用不同的線程類(lèi),不推薦。
上述測(cè)試,我們發(fā)現(xiàn),存在成員變量的類(lèi)用于多線程時(shí)是不安全的,不安全體現(xiàn)在這個(gè)成員變量可能發(fā)生**非原子性的操作**,而變量定義在方法內(nèi)也就是局部變量是線程安全的。想想在使用struts1時(shí),不推薦創(chuàng)建成員變量,因?yàn)閍ction是單例的,如果創(chuàng)建了成員變量,就會(huì)存在線程不安全的隱患,而struts2是每一次請(qǐng)求都會(huì)創(chuàng)建一個(gè)action,就不用考慮線程安全的問(wèn)題。所以,日常開(kāi)發(fā)中,通常需要考慮成員變量或者說(shuō)全局變量在多線程環(huán)境下,是否會(huì)引發(fā)一些問(wèn)題。
- 前言
- Java線程(一):線程安全與不安全
- Java線程(二):線程同步synchronized和volatile
- Java線程(三):線程協(xié)作-生產(chǎn)者/消費(fèi)者問(wèn)題
- Java線程(四):線程中斷、線程讓步、線程睡眠、線程合并
- Java線程(五):Timer和TimerTask
- Java線程(六):線程池
- Java線程(七):Callable和Future
- Java線程(八):鎖對(duì)象Lock-同步問(wèn)題更完美的處理方式
- Java線程(九):Condition-線程通信更高效的方式
- Java線程(十):CAS
- Java線程(十一):Fork/Join-Java并行計(jì)算框架
- Java線程(篇外篇):阻塞隊(duì)列BlockingQueue
- Java線程(篇外篇):線程本地變量ThreadLocal
- Java線程(篇外篇):線程和鎖
