java 加鎖_Java并發之synchronized深入

217a8bfca1650093c9a873f4f2e6d620.png

一句話總結synchronized:

JVM會自動通過使用monitor來加鎖和解鎖,保證了同時只有一個線程可以執行指定代碼,從而保證了線程安全,同時具有可重入和不可中斷的性質。

一.synchronized的作用

使用synchronized修飾方法或者代碼塊時,它能夠保證在同一時刻最多只有一個線程執行該段代碼,以達到保證并發安全的效果。

二.synchronized的地位

1.Synchronized是Java的關鍵字,被Java語言原生支持

2.是最基本的互斥同步手段

3.是并發編程中的元老級角色,是并發編程的主要內容

三.synchronized的性質

synchronized的具有可重入性和不可中斷性

1.可重入性(synchronized區別于其他鎖的一個很重要的性質)

什么是可重入:指的是同一線程的外層函數獲得鎖之后,內層函數可以直接再次獲取該鎖。也叫做遞歸鎖。Java中兩大遞歸鎖:Synchronized和ReentrantLock。

鎖的不可重入性:線程拿到一把鎖了,如果想再次使用這把鎖,必須先將鎖釋放,與其他線程再次進行競爭。

鎖的可重入性:如果線程已經拿到了鎖,試圖去請求這個已經獲得到的鎖,而無需提前釋放,直接就可以使用手里的這把鎖,這就叫做可重入性

synchronized是可重入鎖

可重入鎖的好處:

1 避免死鎖

2 提升封裝性

粒度:

可重入的特性是線程級別的,不是調用級別的。

情況1:證明同一個方法是可重入的

實例代碼:

public class SynchronizedRecursion {

int a = 0;

public static void main(String[] args) {

SynchronizedRecursion sr = new SynchronizedRecursion();

sr.method1();

}

private synchronized void method1(){

System.out.println(synchronized修飾的方法,a = + a);

if(a==0){

a++;

method1();

}

}

}

情況2:證明可重入不要求是同一個方法

實例代碼:

public class SynchronizedOtherMethod {

public synchronized void method1(){

System.out.println(我是method1);

method2();

}

public synchronized void method2(){

System.out.println(我是method2);

}

public static void main(String[] args) {

SynchronizedOtherMethod som = new SynchronizedOtherMethod();

som.method1();

}

}

情況3:證明可重入不要求是同一個類中的

實例代碼:

public class SynchronizedSuperClass {

public synchronized void doSomething(){

System.out.println(我是父類方法);

}

}

class TestClass extends SynchronizedSuperClass{

public synchronized void doSomething(){

System.out.println(我是子類方法);

super.doSomething();

}

public static void main(String[] args) {

TestClass tc = new TestClass();

tc.doSomething();

}

}

2.不可中斷(相比于其他有的鎖可以中斷,這個性質是synchronized的一個劣勢所在)

一旦這個鎖已經被別的線程獲得了,如果當前線程還想獲得,只能選擇等待或者阻塞,直到別的線程釋放這個鎖。如果別的線程 永遠不釋放鎖,那么線程只能永遠地等下去。

相比之下,Lock類,可以擁有中斷的能力。

第一點,如果我覺得我等的時候太長了,有權中斷現在已經獲取到鎖的線程的執行; 第二點,如果我覺得我等待的時間太長了不想再等了,也可以退出。

Lock比synchronized靈活很多,但是編碼易出錯。

四.synchronized的原理

1.加解鎖原理:現象,時機,深入JVM看字節碼

現象:每一個類的實例對應一把鎖,而每一個synchronized方法都必須先獲得調用該方法的類的實例的鎖方能執行,否則線程會阻塞。而方法一旦執行,它就獨占了這把鎖,直到該方法返回或者是拋出異常,才將鎖釋放。一旦鎖釋放之后,之前被阻塞的線程才能獲得這把鎖,從被阻塞的狀態重新進入到可執行的狀態。當一個對象中有synchronized修飾的方法或者代碼塊的時候,要想執行這段代碼,就必須先獲得這個對象鎖,如果此對象的對象鎖已經被其他調用者所占用,就必須等待它被釋放。所有的Java對象都含有一個互斥鎖,這個鎖由JVM自動去獲取和釋放,我們只需要指定這個對象就行了,至于鎖的釋放和獲取不需要我們操心。

獲取和釋放鎖的時機:內置鎖

我們知道每一個Java對象都可以用作一個同步的鎖,這個鎖叫做內部鎖,或者叫做監視器鎖--monitor lock。線程在進入到同步代碼塊之前,會自動獲得這個鎖,并且在退出同步代碼塊的時候會自動的釋放這個鎖,無論是正常途徑退出還是拋出異常退出。獲得這個內置鎖的唯一途徑就是進入這個鎖保護的同步代碼塊或者同步方法中。

實例代碼:

public class SynchronizedToLock {

Lock lock = new ReentrantLock();

public synchronized void method1(){

System.out.println(我是synchronized鎖);

}

public void method2(){

lock.lock(); //加鎖

try{

System.out.println(我是lock鎖);

}finally {

lock.unlock(); //釋放鎖

}

}

public static void main(String[] args) {

SynchronizedToLock stl = new SynchronizedToLock();

stl.method1();

stl.method2();

}

}

method1()和method2()方法等價

深入JVM看字節碼:

實例代碼:

public class Decompilation {

private Object object = new Object();

//同步代碼塊

public void insert(Thread thread){

synchronized (object){

}

}

}

1、將上面的Java代碼編譯為 .class文件,命令窗口執行命令:javac xxx.java 進行編譯。

2、通過反編譯查看字節碼,執行命令:javap -verbose xxx.class 進行反編譯

3、synchronized如何實現的,有個加鎖monitorenter和解鎖monitorexit

讀到該指令,會讓monitor計數器+1或-1

具體如圖:

bbcb005a1bff5cb189a7ac85a55aeb0a.png
8ee56d6e31b888b310316f00c3539a63.png

線程既可以在方法完成之后退出,也可以在拋出異常后退出,因此monitorexit數量多于monitorenter。

monitorenter和monitorexit指令

monitorenter:線程每次進入時,計數器+1。如果重入,繼續加

monitorexit:線程退出時,計數器-1.變為0時候,其他線程可以獲取鎖。

2.可重入原理:加鎖次數計數器

1、JVM負責跟蹤對象被加鎖的次數;

2、有個monitor計數器,線程第一次給對象加鎖的時候,計數變為1.每當這個相同的線程在此對象上再次獲得鎖時,計數會遞增。

3、任務結束離開,則會執行monitorexit,計數遞減,直到完全釋放

3.可見性原理:Java內存模型

dd7eec67c636a0c7e20327e66eb4a9cb.png

五.synchronized的缺陷

1.效率低:

· 鎖的釋放情況少

· 試圖獲得鎖時不能設定超時

· 不能中斷一個正在試圖獲得鎖的線程

2.不夠靈活(讀寫鎖更靈活:讀操作的時候不會加鎖,寫操作的時候才會加鎖):

· 加鎖和釋放的時機單一

· 每個鎖僅有單一的條件(某個對象),可能是不夠的

3.無法知道是否成功獲取到鎖,而使用Lock則不同。

Lock可以,如果嘗試成功了做一些邏輯判斷,如果沒有成功做另外一些邏輯判斷.

Lock類:

lock.lock();lock.unlock();通過這兩個方法,可以手動加鎖和解鎖。

lock.tryLock();lock.tryLock(10, TimeUnit.MINUTES);可以判斷是否加鎖,以及設置超時時間,返回類型為boolean

六.synchronized的用法

1、對象鎖:

a、方法鎖(默認鎖對象為this當前實例對象):synchronized修飾普通方法,鎖對象默認為this

實例代碼:

public synchronized void run(){

for(int j = 0; j 100000; j++){

i++;

}

}

b、同步代碼塊鎖: 自己手動指定鎖對象

實例代碼:

public synchronized void run(){

synchronized (this){

for(int j = 0; j 100000; j++){

i++;

}

}

}

2、類鎖:

Java類可能有很多個對象,但只要一個class對象。類鎖只不過是Class對象的鎖而已。類鎖只能在同一時刻被一個對象擁有。

a、使用關鍵字synchronized修飾靜態static的方法

實例代碼:

public void run(){

method();

}

public static synchronized void method(){

for(int j = 0; j 100000; j++){

i++;

}

}

b、使用關鍵字synchronized指定鎖為Class對象: synchronized(*.class)

實例代碼:

public synchronized void run(){

synchronized (DisappearRequest2.class){

for(int j = 0; j 100000; j++){

i++;

}

}

}

synchronized使用的注意點:

鎖的信息是保存在對象頭中的、作用域不易過大,影響性能、避免死鎖

七.不使用并發情況會帶來的后果以及解決辦法

問題:兩個線程同時a++,最后結果會比預計的少

原因

count++,它看上去只是一個操作,實際上包含了三個動作:

1.讀取count

2.將count加一

3.將count的值寫入到內存中

這三個操作,如果不按照原子去執行,就會帶來并發問題

解決之前:

public class DisappearRequest1 implements Runnable{

static DisappearRequest1 instance = new DisappearRequest1();

static int i = 0;

@Override

public void run(){

for(int j = 0; j 100000; j++){

i++;

}

}

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join(); //等待線程結束

t2.join();

System.out.println(i= + i);

}

}

解決:

public class DisappearRequest2 implements Runnable{

static DisappearRequest2 instance = new DisappearRequest2();

static int i = 0;

//方法1:直接把synchronized加到方法上

/* public synchronized void run(){

for(int j = 0; j 100000; j++){

i++;

}

}*/

//方法2:對象鎖的同步代碼塊

/*public synchronized void run(){

synchronized (this){

for(int j = 0; j 100000; j++){

i++;

}

}

}*/

//第三種:類鎖:synchronized(*.class)

public synchronized void run(){

synchronized (DisappearRequest2.class){

for(int j = 0; j 100000; j++){

i++;

}

}

}

//第四種:類鎖

/*public void run(){

method();

}

public static synchronized void method(){

for(int j = 0; j 100000; j++){

i++;

}

}*/

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join(); //等待線程結束

t2.join();

System.out.println(i= + i);

}

}

八.關于多線程訪問同步方法的七種情況的總結

1.兩個線程同時訪問一個對象的同步方法(對象鎖):串行執行

這種情況就是對象鎖的方法鎖情況。會相互等待,只能有一個線程持有鎖。

2.兩個線程訪問的是兩個對象的同步方法:兩個鎖對象互不干擾,并行執行

不會加鎖,因為訪問的是不同的實例

3.兩個線程訪問的是synchronized的靜態方法:兩個鎖對象是同一個鎖,串行執行

這種情況就是類鎖的靜態方法鎖。

4.同時訪問同步方法與非同步方法:同步(并行)執行

synchronized關鍵字只作用于當前方法,不會影響其他未加關鍵字的方法的并發行為。因此非同步方法不受到影響,還是會并發執行。

5.訪問同一個對象的不同的普通同步方法(對象鎖):串行執行

synchronized關鍵字雖然沒有指定所要的那個鎖對象,但是本質上是指定了this這個對象作為它的鎖。所以對于同一個實例來講,兩個方法拿到的是同一把鎖,因此會出現串行的情況。

6.同時訪問靜態synchronized和非靜態synchronized方法:同步(并行)執行

前者為類鎖,鎖為Class類;后者為對象鎖,鎖為this對象。因此兩者的鎖不同,會并行執行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454279.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454279.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454279.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

激活函數之 Sigmoid、tanh、ReLU、ReLU變形和Maxout

Sigmoid函數 Sigmoid函數計算公式 sigmoid:x取值范圍(-∞,∞),值域是(0, 1)。 sigmoid函數求導 這是sigmoid函數的一個重要性質。 圖像 代碼 # -*- coding: utf-8 -*- """ author: tom """import numpy im…

Python查看、修改pip install 默認使用的pip庫

一、更改 Python 的 pip install 默認使用的pip庫 【方法】只需要將對應python腳本的路徑添加到系統環境變量Path中較前的位置即可,這樣就可以獲得優先級。 1、打開終端,可以通過指令 pip show pip 查看默認使用的pip包: 2、現在&#xff…

cmd 找到8080對應進程_多進程概括

多進程圖像操作系統記錄進程,并按照合理的次序交替推進(分配資源,不斷調度),提高CPU利用率和程序執行速度,這就是操作系統的多進程圖像。當操作系統啟動時,多進程圖像就出現了。 在linux內核源碼main.c文件中&#xff…

機器學習接口和代碼之 KNN

官網地址:https://scikit-learn.org/stable/modules/classes.html#module-sklearn.neighbors class sklearn.neighbors.KNeighborsClassifier(n_neighbors5, weights’uniform’, algorithm’auto’, leaf_size30, p2, metric’minkowski’, metric_paramsNone, n…

Eclipse中安裝Ext插件(Spket IDE)

在網上找了很多資料,這里重新整理一下。 Spket IDE是目前支持Ext 2.0最為出色的IDE。 它采用.jsb project file 文件并將繼承于基類和所有文檔的內容嵌入到生成代碼提示的. doc中。由于Spket只是一個單純的編輯器,沒有其它格式的支持(如CSS&a…

職業程序員培養之道

作者:粘新育 任甲林 來源:希賽網  http://www.csai.cn 2004年06月28日軟件開發是以人為核心的過程,對人的依賴性遠高于傳統的硬件生產企業,為了保持開發能力的穩定性,一方面需要定義軟件過程,以過程為樞紐…

MongoDB啟動

詳細見大神鏈接 鏈接: https://blog.csdn.net/chenpuzhen/article/details/90642147.

Go 語言編程規范

1. gofmt 命令 大部分的格式問題可以通過 gofmt 來解決,gofmt 自動格式化代碼,保證所有的 go 代碼與官方推薦的格式保持一致,所有格式有關問題,都以gofmt的結果為準。所以,建議在提交代碼庫之前先運行一下這個命令。 2…

python去噪音_python中的噪聲是什么意思

你的序列均值為零嗎? 方差隨時間變化嗎? 值與延遲值相關嗎? 你可以用一些工具來檢查你的時間序列是否為白噪音: 創建一個折線圖。檢查總體特征,如變化的平均值,方差或延遲變量之間的明顯關系。 計算匯總統計。對照序列中有意義的連續塊的均值和方差&a…

pycharm 離線安裝插件

插件離線下載地址: http://plugins.jetbrains.com/ 1、下載插件:http://plugins.jetbrains.com/ 2、安裝插件: settings -> plugins -> install plugin from disk,然后重啟IDEA即可。

為機器學習占地16

是將若干個學習器(分類器&回歸器)組合之后產生一個新學習器。弱分類器(weak learner)指那些分類準確率只稍微好于隨機猜測的分類器(errorrate < 0.5)。 集成算法的成功在于保證弱分類器的多樣性(Diversity)。而且集成不穩定的算法也能夠得到一個比較明顯的性能提升。 …

VC DLL學習

1 用VC創建DLL動態鏈接庫1.1 創建dll項目1.2 為dll項目編寫源文件頭文件dllDemo.hextern"C"_declspec(dllexport) intSum(inta,intb);//加法函數。extern"C"_declspec(dllexport) intMax(inta, intb);//取較大值函數extern"C"_declspec(dllexpor…

mciSendString 多線程播放多首音樂 注意事項

昨天晚上遇到一個問題&#xff1a; 使用 mciSendString 控制播放多首音樂的時候&#xff0c;出現最后一次播放的音樂無法通過 mciSendString ("close mp3") 關閉音樂的播放。 mciSendString 在多個線程中調用。 到23點&#xff0c;問題依然沒解決&#xff0c;只好先…

python代碼比例_Python如何輸出百分比

Python 輸出百分比的兩種方式 注&#xff1a; 在python3環境下測試。 方式1&#xff1a;直接使用參數格式化&#xff1a;{:.2%} {:.2%}&#xff1a; 顯示小數點后2位 顯示小數點后2位&#xff1a; >>> print(percent: {:.2%}.format(42/50)) percent: 84.00% 不顯示小…

為機器學習占地15

是將若干個學習器(分類器&回歸器)組合之后產生一個新學習器。弱分類器(weak learner)指那些分類準確率只稍微好于隨機猜測的分類器(errorrat弱分類器的多樣性(Diversity)。而且集成不穩定的算法也能夠得到一個比較明顯的性能提升。 常見的集成學習思想有&#xff1a;Baggi…

編寫一個項目開發文檔

項目開發過程中為了增加程序的可讀性和程序的健壯性&#xff0c; 方便后期程序的調試和維護&#xff0c;所以需要在開發過程中統一技術規范&#xff0c;一般會在項目初期確定好相關文檔作為這一統一的規范。不同公司會對文檔做不同要求&#xff0c;劃不同的分類&#xff0c;但一…

樂在其中設計模式(C#) - 原型模式(Prototype Pattern)

[索引頁][源碼下載]樂在其中設計模式(C#) - 原型模式(Prototype Pattern)作者&#xff1a;webabcd介紹用原型實例指定創建對象的種類&#xff0c;并且通過拷貝這個原型來創建新的對象。示例有一個Message實體類&#xff0c;現在要克隆它。MessageModelusing System; using Syst…

python123添加列表元素_Python之列表

Python變量沒有數據類型&#xff0c;所以Python沒有數組。 整數&#xff1b;浮點數&#xff1b;字符串&#xff1b;對象 創建一個列表&#xff1a; 1.member[大魚,123,3.14,[1,2,3]] 2.empty[] 向列表添加元素&#xff1a; append&#xff08;&#xff09;&#xff1a; member[…

為機器學習占地14

是將若干個學習器(分類器&回歸器)組合之后產生一個新學習器。弱分類器(weak learner)指那些分類準確率只稍微好于隨機猜測的分類器(errorrate <。 集成算法的成功在于保證弱分類器的多樣性(Diversity)。而且集成不穩定的算法也能夠得到一個比較明顯的性能提升。 常見的…

優秀程序員 分析提高能力 程序進階

我出生在南方的一個農村。還記得小時候家里是很窮的&#xff0c;那時候上學也很便宜&#xff0c;我已記不清初中以前的學費是多少了。反正從小在家里玩泥巴&#xff0c;有一日村里兩個女孩去上學&#xff0c;看到我就說一起去上學吧。當時一想&#xff0c;玩泥巴也厭煩了&#…