Java?Synchronized鎖的使用詳解
Synchronized的用法
在多線程并發(fā)問題中,常用Synchronized鎖解決問題。Synchronized鎖通常用于同步示例方法,同步靜態(tài)方法,同步代碼塊等。
同步示例方法
我們可能自己使用過在方法前加Synchronized鎖修飾,在多線程并發(fā)同時調(diào)用同一個實例化對象時,如果這個方法加上了Synchronized鎖,那么也是線程安全的。
舉個栗子:
package Thread; import java.util.stream.IntStream; public class ThreadTest { private Long count=0L; public void incrementCount(){ count++; } public Long execute() throws InterruptedException { Thread thread1=new Thread(()->{ IntStream.range(0,1000).forEach((i)->incrementCount());//線程1循環(huán)1000次 }); Thread thread2=new Thread(()->{ IntStream.range(0,1000).forEach((i)->incrementCount());//線程2循環(huán)1000次 }); thread1.start();//開啟線程 thread2.start(); thread1.join();//等待線程1和線程2執(zhí)行完畢 thread2.join(); return count; } public static void main(String[] args) throws InterruptedException { ThreadTest threadTest=new ThreadTest(); Long count = threadTest.execute(); System.out.println(count); } }
在上面的程序中,count變量為成員變量,在多線程同時使用時極大可能會發(fā)生錯誤,在前面也講到過count++包含三個步驟:1.將變量count從主內(nèi)存中加載到CPU的寄存器中;2.在CPU的寄存器中執(zhí)行count++或++count的操作;3.將運算的count++的結(jié)果寫入緩存或內(nèi)存中。兩個線程都會更新count的值到內(nèi)存中,當其中一個線程再從內(nèi)存中讀取數(shù)據(jù)時,可能讀到的成員變量會與當前的變量不一致,從而使得最終count的結(jié)果不為2000,因此會發(fā)生錯誤。
如何能解決這種錯誤?就是為incrementCount方法加鎖:
public synchronized void incrementCount(){ count++; }
這樣就能保證所得到的count最終值為2000了。
同步靜態(tài)方法
當一個類的某個靜態(tài)方法加了synchronized鎖時,就相當于給這個類的class對象加鎖。所以無論創(chuàng)建多少個當前類的對象調(diào)用這個被synchronized鎖修飾的靜態(tài)方法時,都是線程安全的。
如上面的例子,修改如下:
package Thread; import java.util.stream.IntStream; public class ThreadTest { private static Long count=0L; public static synchronized void incrementCount(){ count++; } public static Long execute() throws InterruptedException { Thread thread1=new Thread(()->{ IntStream.range(0,1000).forEach((i)->incrementCount()); }); Thread thread2=new Thread(()->{ IntStream.range(0,1000).forEach((i)->incrementCount()); }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); return count; } public static void main(String[] args) throws InterruptedException { ThreadTest threadTest=new ThreadTest(); Long count = threadTest.execute(); System.out.println(count); } }
因此,當多個線程并發(fā)執(zhí)行調(diào)用被synchronized鎖修飾的靜態(tài)方法時,這個靜態(tài)方法是線程安全的。
同步代碼塊
前面提到加了synchronized鎖的方法在多線程并發(fā)條件下是線程安全的,但是在執(zhí)行業(yè)務(wù)邏輯過多的代碼塊時,可能會影響程序的執(zhí)行效率。對于此時,可以把一個方法分成多個小的臨界區(qū)。
舉個栗子:
private Long count1=0L; private Long count2=0L; public synchronized void incrementCount(){ count1++; count2++; }
在上面的代碼中,count1和count2為兩個不同的自增操作,因此對于count1和count2來說是兩個不同的臨界區(qū)資源。當一個線程進入incrementCount方法中時,會對整個方法進行加鎖,在對count1進行自增操作時,也會占用count2的資源,相當于占用全部的資源。只有等到這個線程執(zhí)行完count1++和count2++的操作時,釋放鎖時,其它線程才能拿到鎖資源進入incrementCount方法。
但是這樣會影響程序的性能。因為count1++和count2++為兩個互不影響的兩個臨界區(qū)資源,當線程拿到鎖,會占用兩個資源,使得臨界區(qū)資源進行閑置等待,因此可以優(yōu)化代碼,讓synchronized鎖修飾代碼塊。
修改后的代碼:
private Long count1=0L; private Long count2=0L; public Object count1Lock=new Object(); public Object count2Lock=new Object(); public void incrementCount(){ synchronized (count1Lock){ count1++; } synchronized (count2Lock){ count2++; } }
上面代碼中,對count1和count2分別建立了對象鎖count1Lock和count2Lock,而沒有對incrementCount加鎖,意為當一個線程進入incrementCount方法時,其他線程也能進入此方法,當線程1拿到count1Lock對象鎖時,不影響線程2拿到count2Lock對象鎖來對count2執(zhí)行自增操作。
這樣既提高了程序的執(zhí)行效率,同時,由于臨界區(qū)資源都加了鎖,incrementCount方法也是線程安全的。
到此這篇關(guān)于Java Synchronized鎖的使用詳解的文章就介紹到這了,更多相關(guān)Java Synchronized鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java同步鎖Synchronized底層源碼和原理剖析(推薦)
- java同步鎖的正確使用方法(必看篇)
- 95%的Java程序員人都用不好Synchronized詳解
- Java?synchronized同步關(guān)鍵字工作原理
- Java synchronized偏向鎖的概念與使用
- Java?synchronized輕量級鎖實現(xiàn)過程淺析
- Java synchronized重量級鎖實現(xiàn)過程淺析
- Java @Transactional與synchronized使用的問題
- Java?synchronized與死鎖深入探究
- Java synchronized與CAS使用方式詳解
- 淺析Java關(guān)鍵詞synchronized的使用
- synchronized及JUC顯式locks?使用原理解析
- java鎖synchronized面試常問總結(jié)
- Java?HashTable與Collections.synchronizedMap源碼深入解析
- AQS加鎖機制Synchronized相似點詳解
- Java必會的Synchronized底層原理剖析
- 一個例子帶你看懂Java中synchronized關(guān)鍵字到底怎么用
- 詳解Java?Synchronized的實現(xiàn)原理
- Synchronized?和?ReentrantLock?的實現(xiàn)原理及區(qū)別
- Java同步鎖synchronized用法的最全總結(jié)
相關(guān)文章
Hibernate環(huán)境搭建與配置方法(Hello world配置文件版)
這篇文章主要介紹了Hibernate環(huán)境搭建與配置方法,這里演示Hello world配置文件版的具體實現(xiàn)步驟與相關(guān)代碼,需要的朋友可以參考下2016-03-03深入理解Java8新特性之Stream API的創(chuàng)建方式和中間操作步驟
Stream是Java8的一大亮點,是對容器對象功能的增強,它專注于對容器對象進行各種非常便利、高效的 聚合操作(aggregate operation)或者大批量數(shù)據(jù)操作。Stream API借助于同樣新出現(xiàn)的Lambda表達式,極大的提高編程效率和程序可讀性,感興趣的朋友快來看看吧2021-11-11java將XML文檔轉(zhuǎn)換成json格式數(shù)據(jù)的示例
本篇文章主要介紹了java將XML文檔轉(zhuǎn)換成json格式數(shù)據(jù)的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12