Java執行緒同步屬於Java多執行緒與並行程式設計的核心點,需要重點掌握,下面我就來詳解Java執行緒同步的4種主要的實現方式@mikechen
當使用多個執行緒來存取同一個資料時,將會導致資料不準確,相互之間產生衝突,非常容易出現執行緒安全問題,如下圖所示:
比如多個執行緒都在操作同一資料,都打算修改商品庫存,這樣就會導致資料不一致的問題。
執行緒同步的真實意思,其實是「排隊」:幾個執行緒之間要排隊,一個一個對共用資源進行操作,而不是同時進行操作。
所以我們用同步機制來解決這些問題,加入同步鎖以避免在該執行緒沒有完成操作之前,被其他執行緒的呼叫,從而保證了該變數的唯一性和準確性。
這種方式比較靈活,修飾一個程式碼塊,被修飾的程式碼塊稱為同步語句塊。
其作用的範圍是大括號{}括起來的程式碼,作用的物件是呼叫這個程式碼塊的物件,如下格式:
通常沒有必要同步整個方法,使用synchronized程式碼塊同步關鍵程式碼即可。
具體的範例如下:
public class SynchronizedThread { class Bank { private int account = 200; public int getAccount() { return account; } /** * 用同步方法實現 * * @param money */ public synchronized void save(int money) { account += money; } /** * 用同步程式碼塊實現 * * @param money */ public void save1(int money) { synchronized (this) { account += money; } } } class NewThread implements Runnable { private Bank bank; public NewThread(Bank bank) { this.bank = bank; } @Override public void run() { for (int i = 0; i < 10; i++) { // bank.save1(10); bank.save(10); System.out.println(i + "賬戶餘額為:" + bank.getAccount()); } } } /** * 建立執行緒,呼叫內部類 */ public void useThread() { Bank bank = new Bank(); NewThread new_thread = new NewThread(bank); System.out.println("執行緒1"); Thread thread1 = new Thread(new_thread); thread1.start(); System.out.println("執行緒2"); Thread thread2 = new Thread(new_thread); thread2.start(); } public static void main(String[] args) { SynchronizedThread st = new SynchronizedThread(); st.useThread(); } }
ReentrantLock類是可重入、互斥、實現了Lock介面的鎖,它與使用synchronized方法具有相同的基本行為和語意,並且擴充套件了其能力。
synchronized 與 Lock 的對比
ReentrantLock是顯示鎖,手動開啟和關閉鎖,別忘記關閉鎖;
synchronized 是隱式鎖,出了作用域自動釋放;
ReentrantLock只有程式碼塊鎖,synchronized 有程式碼塊鎖和方法鎖;
使用 ReentrantLock鎖,JVM 將花費較少的時間來排程執行緒,執行緒更好,並且具有更好的擴充套件性(提供更多的子類);
優先使用順序:
ReentrantLock> synchronized 同步程式碼塊> synchronized 同步方法
為了完成執行緒同步,我們將使用原子變數(Atomic***開頭的)來實現。
比如典型代表:AtomicInteger類存在於java.util.concurrent.atomic中,該類表示支援原子操作的整數,採用getAndIncrement方法以原子方法將當前的值遞加。
具體範例如下:
private AtomicInteger account = new AtomicInteger(100); public AtomicInteger getAccount() { return account; } public void save(int money) { account.addAndGet(money); }
如果使用ThreadLocal管理變數,則每一個使用該變數的執行緒都獲得該變數的副本,副本之間相互獨立,這樣每一個執行緒都可以隨意修改自己的變數副本,而不會對其他執行緒產生影響,從而實現執行緒同步。
具體程式碼範例如下:
以上
陳睿|mikechen,10年+大廠架構經驗,《BAT架構技術500期》系列文章作者,專注於網際網路架構技術。
閱讀mikechen的網際網路架構更多技術文章合集
Java並行|JVM|MySQL|Spring|Redis|分散式|高並行