Java實現執行緒同步方法及原理詳解
阿新 • • 發佈:2020-06-04
一、概述
無論是什麼語言,在多執行緒程式設計中,常常會遇到多個線同時操作程某個變數(讀/寫),如果讀/寫不同步,則會造成不符合預期的結果。
例如:執行緒A和執行緒B併發執行,都操作變數X,若執行緒A對變數X進行賦上一個新值,執行緒B仍然使用變數X之前的值,很明顯執行緒B使用的X不是我們想要的值了。
Java提供了三種機制,解決上述問題,實現執行緒同步:
同步程式碼塊
synchronized(鎖物件){ // 這裡新增受保護的資料操作 }
同步方法
靜態同步方法:synchronized修飾的靜態方法,它的同步鎖是當前方法所在類的位元組碼物件
public static synchronized void staticMethod(){ }
非靜態同步方法:synchronized修飾的非靜態方法,它的同步鎖即為this
public synchronize void method(){ }
鎖機制
// 以可重入鎖舉例
Lock lock = new ReentrantLock(/*fail*/);
// fail:
// true表示使用公平鎖,即執行緒等待拿到鎖的時間越久,越容易拿到鎖
// false表示使用非公平鎖,執行緒拿到鎖全靠運氣。。。cpu時間片輪到哪個執行緒,哪個執行緒就能獲取鎖
lock.lock();
// 這裡新增受保護的資料操作
lock.unlock();
個人理解:其實無論哪種機制實現執行緒同步,本質上都是加鎖->操作資料->解鎖的過程。同步程式碼塊是針對{}中,同步方法是針對整個方法。其ReentrantLock類提供的lock和unlock和C++的std::mutex提供lock和unlock類似
二、測試用例
同步程式碼塊測試類
package base.synchronize; public class SynchronizeBlock implements Runnable { private int num = 100; @Override public void run() { while (num > 1) { synchronized (this) { // 同步程式碼塊,只有拿到鎖,才有cpu執行權 System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num); num--; } } System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit"); } }
同步方法測試類
package base.synchronize; public class SynchronizeMethod implements Runnable { private int num = 100; public static int staticNum = 100; boolean useStaticMethod; public SynchronizeMethod(boolean useStaticMethodToTest) { this.useStaticMethod = useStaticMethodToTest; } // 對於非靜態方法,同步鎖物件即this public synchronized void method() { System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num); num--; } // 對於靜態方法,同步鎖物件是當前方法所在類的位元組碼物件 public synchronized static void staticMethod() { System.out.println("Static Method Thread ID:" + Thread.currentThread().getId() + "---num:" + staticNum); staticNum--; } @Override public void run() { if (useStaticMethod) { // 測試靜態同步方法 while (staticNum > 1) { staticMethod(); } }else{ // 測試非靜態同步方法 while (num > 1){ method(); } } System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit"); } }
ReentrantLock測試類
package base.synchronize; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SynchronizeLock implements Runnable { private Lock lock = null; private int num = 100; public SynchronizeLock(boolean fair){ lock = new ReentrantLock(fair); // 可重入鎖 } @Override public void run() { while (num > 1) { try { lock.lock(); System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num); num--; } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit"); } }
測試三種機制的Demo
package base.synchronize; public class Demo { public static void main(String[] args) { synchronizeBlockTest(); // 同步程式碼塊 synchronizeMethodTest(); // 同步非靜態方法 synchronizeStaticMethodTest(); // 同步靜態方法 synchronizeLockTest(); // 可重入鎖機制 } public static void synchronizeBlockTest(){ Runnable run = new SynchronizeBlock(); for(int i = 0; i < 3; i++){ new Thread(run).start(); } } public static void synchronizeMethodTest(){ Runnable run = new SynchronizeMethod(false); for(int i = 0; i < 3; i++){ new Thread(run).start(); } } public static void synchronizeStaticMethodTest() { Runnable run = new SynchronizeMethod(true); for(int i = 0; i < 3; i++){ new Thread(run).start(); } } public static void synchronizeLockTest(){ Runnable run = new SynchronizeLock(false); // true:使用公平鎖 false:使用非公平鎖 for(int i = 0; i < 3; i++){ new Thread(run).start(); } } }
無論哪種機制,都得到預期的效果,列印100-0
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。