目錄
一、Synchronized的作用
- 保證同一時刻最多隻有1個線程執行 被Synchronized修飾的方法 / 代碼,
其他線程 必須等待當前線程執行完該方法 / 代碼塊後才能執行該方法 / 代碼塊。
二、 應用場景
保證線程安全,解決多線程中的併發同步問題(實現的是阻塞型併發),具體場景如下:
- 修飾 實例方法 / 代碼塊時,(同步)保護的是同一個對象方法的調用 & 當前實例對象
- 修飾 靜態方法 / 代碼塊時,(同步)保護的是 靜態方法的調用 & class 類對象
三、 原理
- 依賴 JVM 實現同步
- 底層通過一個監視器對象(monitor)完成, wait()、notify() 等方法也依賴於 monitor 對象
監視器鎖(monitor)的本質 依賴於 底層操作系統的互斥鎖(Mutex Lock)實現
四、 具體使用
Synchronized
用於 修飾 代碼塊、類的實例方法 & 靜態方法
1、使用規則
2、鎖的類型 & 等級
-
由於Synchronized 會修飾 代碼塊、類的實例方法 & 靜態方法,故分爲不同鎖的類型
具體如下:
-
之間的區別
3、使用方式
/**
* 對象鎖
*/
public class Test{
// 對象鎖:形式1(方法鎖)
public synchronized void Method1(){
System.out.println("我是對象鎖也是方法鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 對象鎖:形式2(代碼塊形式)
public void Method2(){
synchronized (this){
System.out.println("我是對象鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
* 方法鎖(即對象鎖中的形式1)
*/
public synchronized void Method1(){
System.out.println("我是對象鎖也是方法鎖");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 類鎖
*/
public class Test{
// 類鎖:形式1 :鎖靜態方法
public static synchronized void Method1(){
System.out.println("我是類鎖一號");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 類鎖:形式2 :鎖靜態代碼塊
public void Method2(){
synchronized (Test.class){
System.out.println("我是類鎖二號");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
4、特別注意
Synchronized修飾方法時存在缺陷:若修飾1個大的方法,將會大大影響效率
-
示例
若使用Synchronized關鍵字修飾 線程類的run(),由於run()在線程的整個生命期內一直在運行,因此將導致它對本類任何Synchronized方法的調用都永遠不會成功 -
解決方案
使用 Synchronized關鍵字聲明代碼塊
該解決方案靈活性高:可針對任意代碼塊 & 任意指定上鎖的對象
代碼如下:
synchronized(syncObject) {
// 訪問或修改被鎖保護的共享狀態
// 上述方法 必須 獲得對象 syncObject(類實例或類)的鎖
}
五、特點
注:原子性、可見性、有序性的定義
六、其他控制併發 / 線程同步方式
1、Lock、ReentrantLock(重入鎖)
- 簡介
- 區別
2、CAS
2.1 定義
Compare And Swap
,即 比較 並 交換,是一種解決併發操作的樂觀鎖
synchronized鎖住的代碼塊:同一時刻只能由一個線程訪問,屬於悲觀鎖
2.2 原理
// CAS的操作參數
內存位置(A)
預期原值(B)
預期新值(C)
// 使用CAS解決併發的原理:
// 1. 首先比較A、B,若相等,則更新A中的值爲C、返回True;若不相等,則返回false;
// 2. 通過死循環,以不斷嘗試嘗試更新的方式實現併發
// 僞代碼如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
if(memoryA.get() == oldB){
memoryA.set(newC);
return true;
}
return false;
}
2.3 優點
- 資源耗費少:相對於synchronized,省去了掛起線程、恢復線程的開銷,但,若遲遲得不到更新,死循環對CPU資源也是一種浪費
2.4 具體實現方式
- 使用CAS有個“先檢查後執行”的操作
- 而這種操作在Java中是典型的不安全的操作,所以 CAS在實際中是由C++通過調用CPU指令實現的
- 具體過程
// 1. CAS在Java中的體現爲Unsafe類
// 2. Unsafe類會通過C++直接獲取到屬性的內存地址
// 3. 接下來CAS由C++的Atomic::cmpxchg系列方法實現
2.5 典型應用:AtomicInteger
對 i++ 與 i–,通過compareAndSet & 一個死循環實現
而compareAndSet函數內部 = 通過jni操作CAS指令。直到CAS操作成功跳出循環
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;
}
}