您好,登錄后才能下訂單哦!
Java中synchronized關鍵字的作用分析,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
一、簡介:
synchronized關鍵字是用來控制線程同步的,就是在多線程的環境下,控制synchronized代碼段不被多個線程同時執行。synchronized既可以加在一段代碼上,也可以加在方法上。
1、舉個栗子:
2、類文件編譯
看看該class文件的編譯后的文件,查看當前類的包,變量信息,堆棧信息等等,有助于理解synchronized到底是什么指令操作的:
命令:javap -verbose MySynchronized > mySynchronized.txt
可以看到jvm加鎖的指令,monitorenter加鎖,monitorexit釋放鎖的指令,至于怎么計數的,有興趣的可以深入研究一下。
二、synchronized的方式概念:
類鎖,對象鎖,代碼塊鎖,同步方法鎖。
類鎖:顧名思義是該類的鎖,是初始化靜態方法上的鎖,與對象鎖不一樣,簡單可以理解,類加載完就存在的鎖。
對象鎖:是new出來的實例,每個實例相互隔離。
方法鎖:就是方法上加一個synchronized關鍵字,又區分為靜態方法鎖和實例方法鎖,兩者不一樣。
代碼塊鎖:就是鎖一段代碼,主要區別在括號里的對象synchronized(this){...},或者其他synchronized(lock){...}。
三、鎖的幾種用法和多線程下的阻塞分析
1、代碼塊鎖,新建一個對象:Object lock = new Object(),這里鎖的是這個對象的下的Object 對象,大部分人喜歡這樣用,this是鎖整個對象,范圍比較大,可能造成該對象中其他加鎖方法被干擾,所以可以用這種方式去防止大類對象被使用的時候造成死鎖。
private Object lock = new Object();
/**
* 鎖對象lock
*/
public void methodA(){
synchronized(lock){
System.out.println("methodA====start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA====end");
}
}
2、代碼塊鎖,鎖類對象鎖本身,不同的對象相互隔離,互不干擾。
/**
* 鎖對象本身
*/
public void methodB(){
synchronized(this){
System.out.println("methodB==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB==end");
}
}
3、實例方法鎖:
/**
* 實例方法鎖,同一個實例,多線程阻塞
* 不同的實例,互不干擾,因為鎖的不是一個對象
* 不同的實例,不同實例方法鎖,多線程也是阻塞等待的,因為是一個對象
*/
public synchronized void methodD(){
System.out.println("methodD==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodD==end");
}
4、靜態方法鎖:
/**
* static方法,類級別的鎖,更對象this無關,
* 同一個類中靜態方法鎖,多線程阻塞,等待獲取類鎖才能執行
* 同一個類中不同靜態方法鎖,多線程阻塞,也需要等待獲取類鎖才能執行
*/
public synchronized static void methodC(){
System.out.println("methodC==start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodC==end");
}
5、最后說說wait和sleep和對象鎖:
/**
* wait 會放棄對象鎖,不會放棄CPU資源
* sleep 不會放棄對象鎖,會放棄CPU資源
* 測試發現同一個對象,sleep的方法阻塞,wait方法會放棄對象 * 鎖,繼續多線程執行
*/
public synchronized void methodE(){
System.out.println("methodE==start");
try {
this.wait(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodE==end");
}
四、最后總結:
主要看synchronized 鎖的是不是同一個對象,是一個對象,則下一個線程則需要等待上一個線程釋放對象鎖,(記住是對象鎖)
五、什么場景用?
synchronized 雖然能保證線程安全,但是在并發場景下,會影響性能,比如在搶購場景下。還有加鎖的方法或者加鎖代碼塊是不是一個很耗時的流程,或者一個公共方法,很多業務邏輯都用到,這時候,并發環境下,基本行不通,不同的業務場景下不應該有干擾。所以盡量根據不同的場景和業務邏輯謹慎使用,也可以通過其他方式保證線程的安全,比如用并發包中的一些數據結構,或者ThreadLocal實現線程上下文變量一致性,防止其他程序對公共變量進行篡改。
六、擴展知識
1、很多人問,synchronized 和 實現Lock接口的對象都有加鎖的功能,在開發的時候如何選擇?
《深入理解Java虛擬機》線程安全和鎖優化一章有簡單做介紹,synchronized 和ReentrantLock在JDK1.5 中單核,ReentrantLock比synchronized 的性能要好,多核CPU 下synchronized還是比ReentrantLock好,這樣可以看出synchronized 的性能并不差,所以在JDK1.6,JVM在synchronized 的優化,性能已經由于Lock,在一般的synchronized 能滿足需求的情況下會使用synchronized 。
2、那Lock又有哪些優勢呢?
synchronized 是java 的原生鎖,而ReentrantLock是基于API的方式進行加鎖的,和一些鎖的其他操作。功能比synchronized 要強大,那就意味著用起來就靈活,可以實現很多synchronized 沒有的功能,例如:
1.等待過程中,可以直接中斷等待(如果一直沒有獲得鎖的話)
2.公平鎖:基于時間先后進行鎖獲取,不是synchronized 一樣無序競爭。
3.一個ReentrantLock對象可以綁定多個鎖對象,synchronized (Object)需要多個對象,而ReentrantLock只需多次調用newCondition()方法即可。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。