引入1:計算機記憶體模型

1、因為向主記憶體中讀寫資料的速度要遠遠小于CPU處理資料的速度,所有引入了高速快取的概念(高度快取中讀寫資料的速度相接近CPU處理資料的速度)
2、將主記憶體中的資料讀到高速快取中,再由高速快取讀到暫存器中進行操作,操作成功后,再由暫存器重繪到高速快取,最后再由高速快取重繪到主記憶體(不是立即),這樣以來對主記憶體中讀寫資料緩慢問題就不會影響到CPU的執行效率
3、當多個CPU的運算內容都涉到主記憶體中的相關資料時,將可能導致各自高速快取中資料不一致的問題
引入2:JVM中定義了Java Memory Model的規范,該規范屏蔽了Java應用程式在各個作業系統中對主記憶體訪問的差異性,但并不能保證在多線成環境下對主記憶體訪問的一致性和指令重排問題
堆疊:隨著執行緒的創建而創建,執行緒執行完畢堆疊也就銷毀了
堆:新生代、老年代、永久代(1.8之前對方法區的實作)
方法區:1.8開始使用元空間實作,使用的是本地物理記憶體
引入3:Java記憶體模型和硬體記憶體模型的關聯

1、執行緒之間的共享變數存盤于主記憶體中
2、每個執行緒的堆疊(私有作業記憶體)優先存盤于暫存器或高速快取中,其次才是主記憶體中
3、JMM規定:1、所有變數(除區域變數)都存盤在主記憶體,2、因為執行緒不能直接操作主記憶體中的共享變數,所以每個執行緒在創建的時候都會為其分配一個私有的作業記憶體(堆疊),只能將主記憶體中的共享變數讀取到作業記憶體中才能進行操作,操作成功后再由作業記憶體同步到主記憶體中,
非鎖機制
- volatile關鍵字
volatile關鍵字修飾的變數會強制執行緒從主記憶體中讀取共享變數,操作程序中共享變數發生變化時會立即重繪到主記憶體,從而保證了共享變數在多執行緒環境的可見性,但并不能保證該共享變數復合操作的原子性 - Atomic原子類
Atomic原子類只能保證共享變數在多執行緒下的原子性,底層采用CAS(compare and set)非鎖機制:讀取當前變數的值為A,經過計算后變為B,再寫入當前變數之前需要判斷該變數的值是否改變,沒有改變則更新,改變則獲取改變后的值再走一遍之前的流程,因為比較的是變數值是否改變,所以會出現ABA的問題,即當前變數改變后的值和之前一樣,可以通過為該變數加版本號的形式來區分
鎖機制(JVM鎖)
synchronized關鍵字和ReentrantLock類都是通過加鎖來保證同一時刻只有一個執行緒執行同步代碼,并且在鎖釋放之前將執行緒作業記憶體中的共享變數同步到主記憶體中
- synchronized 關鍵字
@RestController
@RequestMapping("/wx/hkSeckill")
public class HkSeckillControler {
@Resource
private HkSeckillService hkSeckillService;
// http://127.0.0.1:8080/warboot/wx/hkSeckill/seckill
@RequestMapping("seckill")
public JSONObject seckill() {
return hkSeckillService.seckillBySynchronized();
}
}
//
public interface HkSeckillService extends IService<HkSeckillEntity> {
JSONObject seckillBySynchronized();
}
//
public class HkSeckillServiceImpl extends ServiceImpl<HkSeckillMapper, HkSeckillEntity> implements HkSeckillService {
@Resource
private HkService hkService;
@Override
public synchronized JSONObject seckillBySynchronized() {
// TODO 查詢庫存資訊
QueryWrapper<HkEntity> hkEntityQueryWrapper = new QueryWrapper<HkEntity>();
hkEntityQueryWrapper.eq("id", 1);
HkEntity hkEntity = hkService.getOne(hkEntityQueryWrapper);
int count = hkEntity.getCount();
if (--count >= 0) {
// TODO 新增搶單記錄
HkSeckillEntity HkSeckillEntity = new HkSeckillEntity();
HkSeckillEntity.setOpenid(Thread.currentThread().getName());
HkSeckillEntity.setStock(count);
this.save(HkSeckillEntity);
// TODO 庫存減1
UpdateWrapper<HkEntity> updateWrapper = new UpdateWrapper<HkEntity>();
updateWrapper.set("count", count);
updateWrapper.eq("id", hkEntity.getId());
hkService.update(updateWrapper);
//
return ResponseUtil.success();
} else {
return ResponseUtil.fail("搶完了");
}
}
}

- ReentrantLock 類
@RestController
@RequestMapping("/wx/hkSeckill")
public class HkSeckillControler {
@Resource
private HkSeckillService hkSeckillService;
// http://127.0.0.1:8080/warboot/wx/hkSeckill/seckill
@RequestMapping("seckill")
public JSONObject seckill() {
return hkSeckillService.seckillByReentrantLock();
}
}
//
public interface HkSeckillService extends IService<HkSeckillEntity> {
JSONObject seckillByReentrantLock();
}
//
@Service
public class HkSeckillServiceImpl extends ServiceImpl<HkSeckillMapper, HkSeckillEntity> implements HkSeckillService {
@Resource
private HkService hkService;
// TODO 創建顯示鎖,鎖的是當前物件
Lock lock = new ReentrantLock();
@Override
public JSONObject seckillByReentrantLock() {
boolean flag = true;
try {
flag = lock.tryLock(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (flag) {
// TODO 查詢庫存資訊
QueryWrapper<HkEntity> hkEntityQueryWrapper = new QueryWrapper<HkEntity>();
hkEntityQueryWrapper.eq("id", 1);
HkEntity hkEntity = hkService.getOne(hkEntityQueryWrapper);
int count = hkEntity.getCount();
if (--count >= 0) {
try {
// TODO 新增搶單記錄
HkSeckillEntity HkSeckillEntity = new HkSeckillEntity();
HkSeckillEntity.setOpenid(Thread.currentThread().getName());
HkSeckillEntity.setStock(count);
this.save(HkSeckillEntity);
// TODO 庫存減1
UpdateWrapper<HkEntity> updateWrapper = new UpdateWrapper<HkEntity>();
updateWrapper.set("count", count);
updateWrapper.eq("id", hkEntity.getId());
hkService.update(updateWrapper);
} catch (Exception e) {
lock.unlock();
throw new BaseExceptionUtil(e.getMessage());
}
lock.unlock();
return ResponseUtil.success();
} else {
lock.unlock();
return ResponseUtil.fail("搶完了");
}
} else {
return ResponseUtil.fail("獲取鎖超時");
}
}
總結:
synchronized 關鍵字
1、在保證執行緒安全的同時會導致其它執行緒的阻塞
2、執行緒執行成功后或執行程序中發生例外都會自動釋放鎖
3、修飾普通方法時鎖的是當前實體物件;synchronized (this) {} 同步代碼塊
4、修飾靜態方法時鎖的是所有實體物件;synchronized (類.class) {} 同步代碼塊
ReentrantLock 類
1、執行緒取鎖失敗后會進入等待狀態,超過指定時間后會直接回傳false,而不會像synchronized一樣阻塞其它執行緒
2、程式執行完畢或者出現例外時需要手動釋放鎖,否則會出現死鎖
3、可中斷鎖
4、默認采用非公平鎖,根據需求可以設定成公平鎖,而synchronized只能是非公平鎖
公平鎖:執行緒取鎖失敗后會進入等待佇列,先進入佇列的執行緒會先獲得鎖
非公平鎖:執行緒取鎖失敗后會進入等待佇列,但等待執行緒取鎖的概率是隨機的
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/344117.html
標籤:其他
