新聞中心
之前我講了關(guān)于 線程基礎(chǔ)方面的相關(guān)知識,本篇文章將會(huì)帶著大家來學(xué)習(xí)下線程安全相關(guān)的知識。

成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括金寨網(wǎng)站建設(shè)、金寨網(wǎng)站制作、金寨網(wǎng)頁制作以及金寨網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,金寨網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到金寨省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
1 多線程下為什么會(huì)存在線程安全問題
線程的合理使用能夠提升程序的處理性能,一是能夠利用多核 CPU 來實(shí)現(xiàn)線程的并行執(zhí)行,二是線程的異步化執(zhí)行能夠提高系統(tǒng)的吞吐量。
雖然線程有這些優(yōu)點(diǎn),但同時(shí)也帶來了很多問題。比如說:
1.1 共享變量帶來的安全性問題
先來看個(gè)圖:
一個(gè)變量 i ,如果線程 A 或者線程 B 單獨(dú)訪問并且修改變量 i 的值沒有任何問題,那如果并行的修改變量 i ,那就會(huì)有安全性問題。
然后用代碼來模擬一下這種場景,為了更好的看到效果,我用100個(gè)線程:
- public class ThreadDemo1 {
- private static int i = 0;
- public static void inc() {
- try {
- Thread.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- i++;
- }
- public static void main(String[] args) throws InterruptedException {
- for (int i = 0; i < 100; i++) {
- new Thread(() -> ThreadDemo1.inc()).start();
- }
- Thread.sleep(1000);
- System.out.println("運(yùn)行結(jié)果" + i);
- }
- }
輸出結(jié)果:
88
這個(gè)輸出結(jié)果是不固定的,第一次可能是 88 ,第二次可能是 87 ,這個(gè)結(jié)果就和我們預(yù)期的結(jié)果不一致(預(yù)期結(jié)果是100),所以一個(gè)對象是否是線程安全的,取決于它是否會(huì)被多個(gè)線程訪問,以及程序中是如何去使用這個(gè)對象的。如果 多個(gè)線程訪問同一個(gè)共享對象,在不需額外的同步以及調(diào)用端代碼不用做其他協(xié)調(diào)的情況下,這個(gè)共享對象的狀態(tài) 依然是正確的(正確性意味著這個(gè)對象的結(jié)果與我們預(yù)期 規(guī)定的結(jié)果保持一致),那說明這個(gè)對象是線程安全的。
對于線程安全性,本質(zhì)上是管理對于數(shù)據(jù)狀態(tài)的訪問,而且這個(gè)這個(gè)狀態(tài)通常是共享的、可變的。共享:是指這個(gè) 數(shù)據(jù)變量可以被多個(gè)線程訪問;可變:指這個(gè)變量的值在 它的生命周期內(nèi)是可以改變的。
2.如何保證線程并行的數(shù)據(jù)安全性-Synchroinzed
針對上面那種情況,我們該如何解決這種問題呢?首先想到的就是加鎖,并且這種鎖必須是互斥的。比如上面的圖片的例子,如果線程A在修改 i 的值時(shí),線程 B 就不能去修改 i 的值。也就是說并行去修改共享變量的值會(huì)有線程安全性問題,那么我們不讓你并行,不就解決了這個(gè)問題嘛。所以java提供了 Synchroinzed 關(guān)鍵字。
2.1 Synchroinzed 的基本認(rèn)識
Synchroinzed 很早就有了,只是之前是重量級鎖,所以很好有人使用。在 javaSE 1.6 對Synchroinzed進(jìn)行了優(yōu)化引入了偏向鎖和輕量級鎖。所以在并發(fā)量不高的情況還是推薦使用 Synchroinzed 來加鎖。為什么是并發(fā)量不高的情況推薦使用,因?yàn)椴l(fā)量高的情況 Synchroinzed 會(huì)升級為重量級鎖。
2.2 Synchroinzed 的三種加鎖方式
- 修飾實(shí)例方法,鎖是當(dāng)前實(shí)例對象 ,進(jìn)入同步代碼前要獲得當(dāng)前實(shí)例的鎖
- 修飾靜態(tài)方法,鎖是當(dāng)前類的class對象 ,進(jìn)入同步代碼前要獲得當(dāng)前類對象的鎖
- 修飾代碼塊,鎖是括號里面的對象,對給定對象加鎖,進(jìn)入同步代碼庫前要獲得給定對象的鎖。
看下簡單的代碼
- public class SynchroinzedDemo {
- /**
- * 對靜態(tài)方法加鎖
- */
- public static synchronized void test(){}
- /**
- * 對實(shí)例方法加鎖
- */
- public synchronized void test1(){}
- /**
- * 對代碼塊加鎖
- */
- public void test2(){
- synchronized(this){}
- }
- }
然后我們將上面的例子實(shí)現(xiàn) synchronized 加鎖:
- public class ThreadDemo1 {
- private static int i = 0;
- public static void inc() {
- synchronized (ThreadDemo1.class){
- try {
- Thread.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- i++;
- }
- }
- public static void main(String[] args) throws InterruptedException {
- for (int i = 0; i < 100; i++) {
- new Thread(() -> ThreadDemo1.inc()).start();
- }
- Thread.sleep(1000);
- System.out.println("運(yùn)行結(jié)果" + i);
- }
- }
運(yùn)行結(jié)果:
運(yùn)行結(jié)果100
完美的解決共享變量并行修改帶來的線程安全問題。
3 總結(jié)
本文帶著大家了解了一下線程的安全性問題和解決線程安全性問題的 synchronized 關(guān)鍵字的用法。后面的并發(fā)編程系列會(huì)講解更多的解決線程安全性的方法。敬請期待!
分享題目:線程安全之Synchronized關(guān)鍵字
轉(zhuǎn)載來于:http://www.5511xx.com/article/dpdhdho.html


咨詢
建站咨詢
