首先舉一個例子
現在有5個用戶同時訪問業務需要生成5個唯一訂單ID並存入數據庫
這是一個公共的生成ID的類,生成的規則是【當前時間+用於自增的全局變量】(不要在意這個方式的弊端,只是用來舉個栗子~)
我們跑一下試試
可以看到竟然有兩個一模一樣的ID,這是萬萬不能允許發生的情況
爲什麼會發生這種情況呢,是因爲多個線程在同一時間訪問了這個方法,然後修改了這個int變量,上一個線程還沒來得及做完所有操作,int值就被另個線程給修改了
下面講解如何解決問題
基於JVM解決:
1.使用Synchronized解決問題
public class Test{
//創建鎖對象
static synchronizedTest instance=new synchronizedTest();
public void test() {
//省略其他的耗時操作。。。
//使用同步代碼塊對方法內的代碼進行同步操作,鎖對象爲instance
synchronized(instance){
//需要執行的代碼。。。
}
}
}
給生成ID的代碼加上同步代碼塊,成功解決問題
使用同步代碼塊的作用是:同一時刻,只有一個線程可以執行該代碼塊
除了第一種方法外,還可以使用第二種方法解決問題
2.使用Lock鎖解決問題
public class LockTest {
private Lock lock = new ReentrantLock();
//需要參與同步的方法
private void test(Thread thread){
//獲取鎖,如果鎖被暫用則一直等待
lock.lock();
try {
System.out.println("獲得了鎖");
}catch(Exception e){
//打印異常
e.printStackTrace();
} finally {
System.out.println("釋放了鎖");
//釋放鎖
lock.unlock();
}
}
給生成ID的代碼加上Lock鎖,成功解決問題
這兩種方法都是爲了解決同步問題,那麼他們的區別是什麼呢?
這裏提供一個表來方便做對比(來源:https://blog.csdn.net/qq_39521554/article/details/81130442)
類別 | synchronized | Lock |
---|---|---|
存在層次 | Java的關鍵字,在jvm層面上 | 是一個類 |
鎖的釋放 | 1、以獲取鎖的線程執行完同步代碼,釋放鎖 2、線程執行發生異常,jvm會讓線程釋放鎖 | 在finally中必須釋放鎖,不然容易造成線程死鎖 |
鎖的獲取 | 假設A線程獲得鎖,B線程等待。如果A線程阻塞,B線程會一直等待 | 分情況而定,Lock有多個鎖獲取的方式,具體下面會說道,大致就是可以嘗試獲得鎖,線程可以不用一直等待 |
鎖狀態 | 無法判斷 | 可以判斷 |
鎖類型 | 可重入 不可中斷 非公平 | 可重入 可判斷 可公平(兩者皆可) |
性能 | 少量同步 | 大量同步 |
談談我的看法:
兩者有一個很大的區別就是Synchronized是自動釋放鎖的,而ReentrantLock需要手動通過unlock()釋放,如果沒有處理好就很容易死鎖
其次,ReentrantLock鎖機制要比Synchronized更好一些,比如Synchronized因IO等問題被阻塞了,但是又沒有釋放鎖,其他線程便只能乾巴巴的等着,ReentrantLock有機制可以使線程不無限期等待而Synchronized不可以
兩種同步方式的使用建議:
Synchronized和ReentrantLock在一般情況下沒有什麼區別,但是在非常複雜的同步應用中,請考慮使用ReentrantLock,特別是遇到下面3種需求
1.某個線程在等待一個鎖的控制權的這段時間需要中斷
2.需要分開處理一些wait-notify,ReentrantLock裏面的Condition應用,能夠控制notify哪個線程
3.具有公平鎖功能,每個到來的線程都將排隊等候
下一次說說分佈式的解決方案
這是我通過學習對知識的整理及備忘,本博客的所有內容,僅是自己的一些學習筆記,如有錯誤,歡迎指正。如有侵權,請告知修改。