引出
1.大量請(qǐng)求擁擠搶購(gòu)中的數(shù)據(jù)不安全問(wèn)題;
2.事務(wù)ACID:原子性(Atomicity)一致性(Consistency)隔離性(Isolation)持久性(Durability);
3.線程安全特征:原子性(Atomicity)可見(jiàn)性(Visibility)有序性(Ordering);
4.java中的鎖初步,synchronize鎖和ReentrantLock鎖使用初步;
5.濫用鎖的問(wèn)題,以及產(chǎn)生死鎖的條件;
場(chǎng)景:大量請(qǐng)求擁擠搶購(gòu)
package com.tianju.redis.service.impl;
import com.tianju.redis.service.IRushGoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RushGoodsServiceImpl implements IRushGoodsService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private final String GOODS = "goods";
@Override
public String rush() {
String sNum = stringRedisTemplate.opsForValue().get(GOODS);
int nums = Integer.parseInt(sNum);
if (nums>0){
stringRedisTemplate.opsForValue().set(GOODS, String.valueOf(--nums) );
return stringRedisTemplate.opsForValue().get(GOODS);
}else {
return "error";
}
}
}
@PutMapping("/rushJmeter")
public void rushJmeter(){
String goodsNum = rushGoodsService.rush();
System.out.println("goodsNum: "+goodsNum);
}
事務(wù)的基本特征ACID
事務(wù)是指一組操作被視為一個(gè)不可分割的工作單元,要么全部執(zhí)行成功,要么全部不執(zhí)行。事務(wù)具有以下四個(gè)基本特征,通常被稱為ACID特性:
-
原子性(Atomicity):事務(wù)是一個(gè)原子操作,要么全部執(zhí)行成功,要么全部不執(zhí)行。如果事務(wù)中的任何一個(gè)操作失敗,整個(gè)事務(wù)將被回滾到初始狀態(tài),不會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生任何影響。
-
一致性(Consistency):事務(wù)在執(zhí)行前和執(zhí)行后,數(shù)據(jù)庫(kù)的狀態(tài)必須保持一致。這意味著事務(wù)中的操作必須滿足數(shù)據(jù)庫(kù)的完整性約束,包括唯一性約束、外鍵約束等。
-
隔離性(Isolation):事務(wù)的執(zhí)行是相互隔離的,一個(gè)事務(wù)的操作不會(huì)被其他事務(wù)所干擾。隔離性確保了并發(fā)執(zhí)行的事務(wù)之間不會(huì)產(chǎn)生不一致的結(jié)果。
-
持久性(Durability):一旦事務(wù)提交成功,其所做的修改將永久保存在數(shù)據(jù)庫(kù)中,即使系統(tǒng)發(fā)生故障或重啟,修改的結(jié)果也不會(huì)丟失。
這些特性確保了事務(wù)的可靠性和一致性。數(shù)據(jù)庫(kù)管理系統(tǒng)通過(guò)使用日志和鎖等機(jī)制來(lái)實(shí)現(xiàn)事務(wù)的特性。在設(shè)計(jì)和實(shí)現(xiàn)數(shù)據(jù)庫(kù)應(yīng)用程序時(shí),需要考慮事務(wù)的邊界和正確使用事務(wù)來(lái)保證數(shù)據(jù)的完整性和一致性
線程安全的基本特征
線程安全是指在多線程環(huán)境下,對(duì)共享資源的訪問(wèn)和操作不會(huì)導(dǎo)致數(shù)據(jù)不一致或產(chǎn)生不可預(yù)期的結(jié)果。線程安全的基本特征包括:
-
原子性(Atomicity):對(duì)共享資源的操作要么全部執(zhí)行成功,要么全部不執(zhí)行,不存在中間狀態(tài)。即使在多線程環(huán)境下,也能保證操作的完整性。簡(jiǎn)單說(shuō)就是相關(guān)操作不會(huì)中途被其他線程干擾,一般通過(guò)同步機(jī)制實(shí)現(xiàn)
-
可見(jiàn)性(Visibility):一個(gè)線程對(duì)共享資源的修改對(duì)其他線程是可見(jiàn)的。當(dāng)一個(gè)線程修改了共享資源的值后,其他線程能夠立即看到最新的值??梢?jiàn)性,是一個(gè)線程修改了某個(gè)共享變量,其狀態(tài)能夠立即被其他線程知曉,通常被解釋為將線程本地狀態(tài)反映到主內(nèi)存上,volatile就是負(fù)責(zé)保證可見(jiàn)性的。
-
有序性(Ordering):線程的執(zhí)行順序與程序的代碼順序一致。即使在多線程環(huán)境下,也能保證操作按照預(yù)期的順序執(zhí)行。是保證線程內(nèi)串行語(yǔ)義,避免指令重排等。
解決辦法
-
使用互斥鎖(Mutex)或信號(hào)量(Semaphore)等同步機(jī)制,確保在同一時(shí)間只有一個(gè)線程能夠訪問(wèn)共享資源。
-
使用原子操作(Atomic Operation)來(lái)保證對(duì)共享資源的操作是原子的,不會(huì)被其他線程中斷。
-
使用volatile關(guān)鍵字來(lái)保證共享變量的可見(jiàn)性,確保一個(gè)線程對(duì)共享變量的修改對(duì)其他線程是可見(jiàn)的。
-
使用線程安全的數(shù)據(jù)結(jié)構(gòu)或類,這些數(shù)據(jù)結(jié)構(gòu)或類已經(jīng)在設(shè)計(jì)上考慮了多線程環(huán)境下的安全性。
加鎖(java)
synchronized鎖
可重入鎖: sychronized ReentrantLock
synchronized (this.getClass()){}
ReentrantLock鎖
private final ReentrantLock lock = new ReentrantLock(); // 可重入鎖
什么是可重入鎖?
當(dāng)線程獲取某個(gè)鎖后,還可以繼續(xù)獲取它,可以遞歸調(diào)用,而不會(huì)發(fā)生死鎖;
如何保證可重入
當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)加鎖和解鎖,只需簡(jiǎn)單測(cè)試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。
如果測(cè)試成功,表示線程已經(jīng)獲得了鎖。
如果測(cè)試失敗,則需要再測(cè)試一下Mark Word中偏向鎖標(biāo)志是否設(shè)置成1:沒(méi)有則CAS競(jìng)爭(zhēng);設(shè)置了,則CAS將對(duì)象頭偏向鎖指向當(dāng)前線程。再維護(hù)一個(gè)計(jì)數(shù)器,同個(gè)線程進(jìn)入則自增1,離開(kāi)再減1,直到為0才能釋放
濫用鎖的代價(jià)?(死鎖)
死鎖的四個(gè)必要條件
循環(huán) A —> B —>C —>A
產(chǎn)生死鎖的必要條件是以下四個(gè)條件同時(shí)滿足:
-
互斥條件(Mutual Exclusion):至少有一個(gè)資源被一個(gè)進(jìn)程獨(dú)占使用,即在一段時(shí)間內(nèi)只能由一個(gè)進(jìn)程訪問(wèn)。
-
請(qǐng)求與保持條件(Hold and Wait):一個(gè)進(jìn)程在持有至少一個(gè)資源的同時(shí),又請(qǐng)求獲取其他進(jìn)程持有的資源。
-
不可剝奪條件(No Preemption):資源只能由持有者顯式地釋放,其他進(jìn)程無(wú)法強(qiáng)制剝奪。
-
循環(huán)等待條件(Circular Wait):存在一個(gè)進(jìn)程資源的循環(huán)鏈,每個(gè)進(jìn)程都在等待下一個(gè)進(jìn)程所持有的資源。
當(dāng)這四個(gè)條件同時(shí)滿足時(shí),就可能發(fā)生死鎖。如果任何一個(gè)條件不滿足,就不會(huì)發(fā)生死鎖。
死鎖是多線程或多進(jìn)程并發(fā)執(zhí)行時(shí)的一種常見(jiàn)問(wèn)題,它會(huì)導(dǎo)致系統(tǒng)無(wú)法繼續(xù)執(zhí)行下去,需要通過(guò)死鎖檢測(cè)、死鎖預(yù)防、死鎖避免或死鎖解除等方法來(lái)處理。
死鎖的案例
可能導(dǎo)致死鎖
鎖對(duì)象ObjLock
package com.tianju.redis.lock;
public class ObjLock {
private String name;
public ObjLock(String name){
this.name = name;
}
@Override
public String toString() {
return "ObjLock:"+this.name;
}
}
加鎖釋放鎖方法DeadLockDemo
package com.tianju.redis.lock;
public class DeadLockDemo {
private ObjLock a;
public ObjLock b;
public DeadLockDemo(ObjLock a,ObjLock b){
this.a = a;
this.b = b;
}
public void dead(){
System.out.println("********"+a+"對(duì)象"+b+"對(duì)象都加鎖**************");
System.out.println(a+"--"+b+": "+"準(zhǔn)備給"+a+"對(duì)象加鎖>>");
synchronized (a){
System.out.println(a+"--"+b+": "+a+"對(duì)象加鎖成功...");
System.out.println(a+"--"+b+": "+"準(zhǔn)備給"+b+"對(duì)象加鎖>>>");
synchronized (b){
System.out.println(a+"--"+b+": "+b+"對(duì)象加鎖成功");
}
System.out.println(a+"--"+b+": "+"釋放"+b+"對(duì)象的鎖");
}
System.out.println(a+"--"+b+": "+"釋放"+b+"對(duì)象的鎖");
System.out.println("****************");
}
}
測(cè)試方法文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-673771.html
package com.tianju.redis.lock;
public class TestDeadLock {
/**
* 一個(gè)一個(gè)順序運(yùn)行
*/
public static void run(){
ObjLock a = new ObjLock("A");
ObjLock b = new ObjLock("B");
ObjLock c = new ObjLock("C");
DeadLockDemo lockDemo1 = new DeadLockDemo(a, b);
lockDemo1.dead(); // 鎖住a和b
DeadLockDemo lockDemo2 = new DeadLockDemo(b, c);
lockDemo2.dead(); // 鎖住a和b
DeadLockDemo lockDemo3 = new DeadLockDemo(c, a);
lockDemo3.dead(); // 鎖住a和b
}
/**
* 進(jìn)行線程搶,死鎖
*/
public static void rushRun(){
ObjLock a = new ObjLock("A");
ObjLock b = new ObjLock("B");
ObjLock c = new ObjLock("C");
new Thread(()->{
DeadLockDemo lockDemo1 = new DeadLockDemo(a, b);
lockDemo1.dead(); // 鎖住a和b
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
new Thread(()->{
DeadLockDemo lockDemo2 = new DeadLockDemo(b, c);
lockDemo2.dead(); // 鎖住a和b
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
new Thread(()->{
DeadLockDemo lockDemo3 = new DeadLockDemo(c, a);
lockDemo3.dead(); // 鎖住a和b
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
public static void main(String[] args) {
// run(); // 順序執(zhí)行加鎖,解鎖
rushRun(); // 線程進(jìn)行搶
}
}
總結(jié)
1.大量請(qǐng)求擁擠搶購(gòu)中的數(shù)據(jù)不安全問(wèn)題;
2.事務(wù)ACID:原子性(Atomicity)一致性(Consistency)隔離性(Isolation)持久性(Durability);
3.線程安全特征:原子性(Atomicity)可見(jiàn)性(Visibility)有序性(Ordering);
4.java中的鎖初步,synchronize鎖和ReentrantLock鎖使用初步;
5.濫用鎖的問(wèn)題,以及產(chǎn)生死鎖的條件;文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-673771.html
到了這里,關(guān)于Java進(jìn)階(6)——搶購(gòu)問(wèn)題中的數(shù)據(jù)不安全(非原子性問(wèn)題)& Java中的synchronize和ReentrantLock鎖使用 & 死鎖及其產(chǎn)生的條件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!