?

Java執行緒同步屬于Java多執行緒與并發編程的核心點,需要重點掌握,下面我就來詳解Java執行緒同步的4種主要的實作方式@mikechen
目錄
- 什么是執行緒同步
- 執行緒同步的幾種方式
- 1、使用synchronized關鍵字
- 2.使用ReentrantLock
- 3.使用原子變數實作執行緒同步
- 4.ThreadLocal實作執行緒同步
什么是執行緒同步
當使用多個執行緒來訪問同一個資料時,將會導致資料不準確,相互之間產生沖突,非常容易出現執行緒安全問題,如下圖所示:

比如多個執行緒都在操作同一資料,都打算修改商品庫存,這樣就會導致資料不一致的問題,
執行緒同步的真實意思,其實是“排隊”:幾個執行緒之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作,
所以我們用同步機制來解決這些問題,加入同步鎖以避免在該執行緒沒有完成操作之前,被其他執行緒的呼叫,從而保證了該變數的唯一性和準確性,
執行緒同步的幾種方式

1、使用synchronized關鍵字
這種方式比較靈活,修飾一個代碼塊,被修飾的代碼塊稱為同步陳述句塊,
其作用的范圍是大括號{}括起來的代碼,作用的物件是呼叫這個代碼塊的物件,如下格式:
synchronized(物件) { //得到物件的鎖,才能操作同步代碼 需要被同步代碼; }

通常沒有必要同步整個方法,使用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(); } }
2.使用ReentrantLock
ReentrantLock類是可重入、互斥、實作了Lock介面的鎖,它與使用synchronized方法具有相同的基本行為和語意,并且擴展了其能力,
private int account = 100; //需要宣告這個鎖 private Lock lock = new ReentrantLock(); public int getAccount() { return account; } //這里不再需要synchronized public void save(int money) { lock.lock(); try{ account += money; }finally{ lock.unlock(); } } }
synchronized 與 Lock 的對比
ReentrantLock是顯示鎖,手動開啟和關閉鎖,別忘記關閉鎖;
synchronized 是隱式鎖,出了作用域自動釋放;
ReentrantLock只有代碼塊鎖,synchronized 有代碼塊鎖和方法鎖;
使用 ReentrantLock鎖,JVM 將花費較少的時間來調度執行緒,執行緒更好,并且具有更好的擴展性(提供更多的子類);
優先使用順序:
ReentrantLock> synchronized 同步代碼塊> synchronized 同步方法
3.使用原子變數實作執行緒同步
為了完成執行緒同步,我們將使用原子變數(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); }

4.ThreadLocal實作執行緒同步
如果使用ThreadLocal管理變數,則每一個使用該變數的執行緒都獲得該變數的副本,副本之間相互獨立,這樣每一個執行緒都可以隨意修改自己的變數副本,而不會對其他執行緒產生影響,從而實作執行緒同步,
具體代碼示例如下:
//只改Bank類,其余代碼與上同 public class Bank{ // 創建一個執行緒本地變數 ThreadLocal private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){ @Override //回傳當前執行緒的"初始值" protected Integer initialValue(){ return 100; } }; public void save(int money){ //設定執行緒副本中的值 account.set(account.get()+money); } public int getAccount(){ //回傳執行緒副本中的值 return account.get(); } }

以上
作者簡介
陳睿|mikechen,10年+大廠架構經驗,《BAT架構技術500期》系列文章作者,專注于互聯網架構技術,
閱讀mikechen的互聯網架構更多技術文章合集
Java并發|JVM|MySQL|Spring|Redis|分布式|高并發?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/509737.html
標籤:其他
下一篇:驅動開發:內核中的自旋鎖結構
