前言
- 在
Java
中,有一個(gè)常被忽略 但 非常重要的關(guān)鍵字Synchronized
- 今天橙困,我將詳細(xì)講解
Java
關(guān)鍵字Synchronized
的所有知識(shí)哮兰,希望你們會(huì)喜歡
Carson帶你學(xué)多線程系列
基礎(chǔ)匯總
Android多線程:基礎(chǔ)知識(shí)匯總
基礎(chǔ)使用
Android多線程:繼承Thread類使用(含實(shí)例教程)
Android多線程:實(shí)現(xiàn)Runnable接口使用(含實(shí)例教程)
復(fù)合使用
Android多線程:AsyncTask使用教程(含實(shí)例講解)
Android多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實(shí)例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實(shí)例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學(xué)
相關(guān)使用
Android異步通信:這是一份全面&詳細(xì)的Handler機(jī)制學(xué)習(xí)攻略
Android多線程:手把手教你全面學(xué)習(xí)神秘的Synchronized關(guān)鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
目錄
1. 定義
Java
中的1個(gè)關(guān)鍵字
2. 作用
保證同一時(shí)刻最多只有1個(gè)線程執(zhí)行 被Synchronized
修飾的方法 / 代碼
其他線程 必須等待當(dāng)前線程執(zhí)行完該方法 / 代碼塊后才能執(zhí)行該方法 / 代碼塊
3. 應(yīng)用場(chǎng)景
保證線程安全,解決多線程中的并發(fā)同步問題(實(shí)現(xiàn)的是阻塞型并發(fā))肖抱,具體場(chǎng)景如下:
- 修飾 實(shí)例方法 / 代碼塊時(shí)奋刽,(同步)保護(hù)的是同一個(gè)對(duì)象方法的調(diào)用 & 當(dāng)前實(shí)例對(duì)象
- 修飾 靜態(tài)方法 / 代碼塊時(shí)瓦侮,(同步)保護(hù)的是 靜態(tài)方法的調(diào)用 & class 類對(duì)象
4. 原理
- 依賴
JVM
實(shí)現(xiàn)同步 - 底層通過一個(gè)監(jiān)視器對(duì)象
(monitor)
完成,wait()
杨名、notify()
等方法也依賴于 monitor 對(duì)象
監(jiān)視器鎖(monitor)的本質(zhì) 依賴于 底層操作系統(tǒng)的互斥鎖(Mutex Lock)實(shí)現(xiàn)
5. 具體使用
Synchronized
用于 修飾 代碼塊脏榆、類的實(shí)例方法 & 靜態(tài)方法
5.1 鎖的類型 & 等級(jí)
- 由于
Synchronized
會(huì)修飾 代碼塊、類的實(shí)例方法 & 靜態(tài)方法台谍,故分為不同鎖的類型 - 具體如下
- 之間的區(qū)別
5.2 使用規(guī)則
5.3 使用方式
/**
* 對(duì)象鎖
*/
public class Test{
// 對(duì)象鎖:形式1(方法鎖)
public synchronized void Method1(){
System.out.println("我是對(duì)象鎖也是方法鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 對(duì)象鎖:形式2(代碼塊形式)
public void Method2(){
synchronized (this){
System.out.println("我是對(duì)象鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
* 方法鎖(即對(duì)象鎖中的形式1)
*/
public synchronized void Method1(){
System.out.println("我是對(duì)象鎖也是方法鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 類鎖
*/
public class Test{
// 類鎖:形式1 :鎖靜態(tài)方法
public static synchronized void Method1(){
System.out.println("我是類鎖一號(hào)");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 類鎖:形式2 :鎖靜態(tài)代碼塊
public void Method2(){
synchronized (Test.class){
System.out.println("我是類鎖二號(hào)");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
5.4 特別注意
Synchronized
修飾方法時(shí)存在缺陷:若修飾1個(gè)大的方法,將會(huì)大大影響效率
示例
若使用Synchronized
關(guān)鍵字修飾 線程類的run()
,由于run()
在線程的整個(gè)生命期內(nèi)一直在運(yùn)行趁蕊,因此將導(dǎo)致它對(duì)本類任何Synchronized
方法的調(diào)用都永遠(yuǎn)不會(huì)成功解決方案
使用Synchronized
關(guān)鍵字聲明代碼塊
該解決方案靈活性高:可針對(duì)任意代碼塊 & 任意指定上鎖的對(duì)象
代碼如下
synchronized(syncObject) {
// 訪問或修改被鎖保護(hù)的共享狀態(tài)
// 上述方法 必須 獲得對(duì)象 syncObject(類實(shí)例或類)的鎖
}
6. 特點(diǎn)
注:原子性坞生、可見性、有序性的定義
7. 其他控制并發(fā) / 線程同步方式
7.1 Lock掷伙、ReentrantLock
- 簡(jiǎn)介
- 區(qū)別
7.2 CAS
7.2.1 定義
Compare And Swap
是己,即 比較 并 交換,是一種解決并發(fā)操作的樂觀鎖
synchronized
鎖住的代碼塊:同一時(shí)刻只能由一個(gè)線程訪問任柜,屬于悲觀鎖
7.2.2 原理
// CAS的操作參數(shù)
內(nèi)存位置(A)
預(yù)期原值(B)
預(yù)期新值(C)
// 使用CAS解決并發(fā)的原理:
// 1. 首先比較A卒废、B,若相等宙地,則更新A中的值為C摔认、返回True;若不相等宅粥,則返回false参袱;
// 2. 通過死循環(huán),以不斷嘗試嘗試更新的方式實(shí)現(xiàn)并發(fā)
// 偽代碼如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
if(memoryA.get() == oldB){
memoryA.set(newC);
return true;
}
return false;
}
7.2.3 優(yōu)點(diǎn)
資源耗費(fèi)少:相對(duì)于synchronized
秽梅,省去了掛起線程抹蚀、恢復(fù)線程的開銷
但,若遲遲得不到更新企垦,死循環(huán)對(duì)
CPU
資源也是一種浪費(fèi)
7.2.4 具體實(shí)現(xiàn)方式
- 使用CAS有個(gè)“先檢查后執(zhí)行”的操作
- 而這種操作在Java中是典型的不安全的操作环壤,所以
CAS
在實(shí)際中是由C++
通過調(diào)用CPU指令實(shí)現(xiàn)的 - 具體過程
// 1. CAS在Java中的體現(xiàn)為Unsafe類
// 2. Unsafe類會(huì)通過C++直接獲取到屬性的內(nèi)存地址
// 3. 接下來CAS由C++的Atomic::cmpxchg系列方法實(shí)現(xiàn)
7.2.5 典型應(yīng)用:AtomicInteger
對(duì) i++ 與 i--,通過compareAndSet
& 一個(gè)死循環(huán)實(shí)現(xiàn)
而
compareAndSet
函數(shù)內(nèi)部 = 通過jni
操作CAS
指令钞诡。直到CAS操作成功跳出循環(huán)
private volatile int value;
/**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
8. 總結(jié)
- 本文主要對(duì)
Java
中常被忽略 但 非常重要的關(guān)鍵字Synchronized
進(jìn)行講解 - 下一篇文章我將對(duì)講解
Android多線程
的相關(guān)知識(shí)镐捧,感興趣的同學(xué)可以繼續(xù)關(guān)注Carson_Ho的簡(jiǎn)書
Carson帶你學(xué)多線程系列
基礎(chǔ)匯總
Android多線程:基礎(chǔ)知識(shí)匯總
基礎(chǔ)使用
Android多線程:繼承Thread類使用(含實(shí)例教程)
Android多線程:實(shí)現(xiàn)Runnable接口使用(含實(shí)例教程)
復(fù)合使用
Android多線程:AsyncTask使用教程(含實(shí)例講解)
Android多線程:AsyncTask原理及源碼分析
Android多線程:HandlerThread使用教程(含實(shí)例講解)
Android多線程:HandlerThread原理及源碼分析
Android多線程:IntentService使用教程(含實(shí)例講解)
Android多線程:IntentService的原理及源碼分析
Android多線程:線程池ThreadPool全方位教學(xué)
相關(guān)使用
Android異步通信:這是一份全面&詳細(xì)的Handler機(jī)制學(xué)習(xí)攻略
Android多線程:手把手教你全面學(xué)習(xí)神秘的Synchronized關(guān)鍵字
Android多線程:帶你了解神秘的線程變量 ThreadLocal
歡迎關(guān)注Carson_Ho的簡(jiǎn)書
不定期分享關(guān)于安卓開發(fā)的干貨,追求短臭增、平懂酱、快,但卻不缺深度誊抛。