前言
在Java中,有一個常被忽略 但 非常重要的關鍵字Synchronized今天,我將詳細講解 Java關鍵字Synchronized的所有知識,希望你們會喜歡
目錄
1. 定義
Java中的1個關鍵字
2. 作用
保證同一時刻最多只有1個線程執行 被Synchronized修飾的方法 / 代碼
其他線程 必須等待當前線程執行完該方法 / 代碼塊后才能執行該方法 / 代碼塊
3. 應用場景
保證線程安全,解決多線程中的并發同步問題(實現的是阻塞型并發),具體場景如下:
修飾 實例方法 / 代碼塊時,(同步)保護的是同一個對象方法的調用 & 當前實例對象修飾 靜態方法 / 代碼塊時,(同步)保護的是 靜態方法的調用 & class 類對象
4. 原理
依賴 JVM 實現同步底層通過一個監視器對象(monitor)完成, wait()、notify() 等方法也依賴于 monitor 對象
監視器鎖(monitor)的本質 依賴于 底層操作系統的互斥鎖(Mutex Lock)實現
5. 具體使用
Synchronized 用于 修飾 代碼塊、類的實例方法 & 靜態方法
5.1 使用規則
5.2 鎖的類型 & 等級 由于Synchronized 會修飾 代碼塊、類的實例方法 & 靜態方法,故分為不同鎖的類型具體如下
之間的區別
5.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();
- }
- }
- }
- }
5.4 特別注意
Synchronized修飾方法時存在缺陷:若修飾1個大的方法,將會大大影響效率
示例
若使用Synchronized關鍵字修飾 線程類的run(),由于run()在線程的整個生命期內一直在運行,因此將導致它對本類任何Synchronized方法的調用都永遠不會成功
解決方案
使用 Synchronized關鍵字聲明代碼塊
該解決方案靈活性高:可針對任意代碼塊 & 任意指定上鎖的對象
- 代碼如下
- synchronized(syncObject) {
- // 訪問或修改被鎖保護的共享狀態
- // 上述方法 必須 獲得對象 syncObject(類實例或類)的鎖
- }
6. 特點
注:原子性、可見性、有序性的定義
7. 其他控制并發 / 線程同步方式
7.1 Lock、ReentrantLock 簡介
區別
7.2 CAS
7.2.1 定義
Compare And Swap,即 比較 并 交換,是一種解決并發操作的樂觀鎖
synchronized鎖住的代碼塊:同一時刻只能由一個線程訪問,屬于悲觀鎖
7.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;
- }
7.2.3 優點
資源耗費少:相對于synchronized,省去了掛起線程、恢復線程的開銷
但,若遲遲得不到更新,死循環對CPU資源也是一種浪費
7.2.4 具體實現方式 使用CAS有個“先檢查后執行”的操作而這種操作在Java中是典型的不安全的操作,所以 CAS在實際中是由C++通過調用CPU指令實現的具體過程
- // 1. CAS在Java中的體現為Unsafe類
- // 2. Unsafe類會通過C++直接獲取到屬性的內存地址
- // 3. 接下來CAS由C++的Atomic::cmpxchg系列方法實現
7.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;
- }
- }
8. 總結
本文主要對Java中常被忽略 但 非常重要的關鍵字Synchronized進行講解
到此這篇關于Java的Synchronized關鍵字學習指南的文章就介紹到這了,更多相關Java的Synchronized關鍵字內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/carson_ho/article/details/82992269