傳統(tǒng)的Synchronized鎖
線程就是一個(gè)單獨(dú)的資源類横朋,它沒有任何的附屬操作寂祥!
先看不加Synchronized多線程并發(fā)下的買票問題:
public class SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//并發(fā)墩新,多個(gè)線程操作同一個(gè)資源類倍谜,將類丟入線程崩泡。
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"C").start();
}
}
class Ticket{
/*票數(shù)*/
private int number = 30;
/*買票的方式*/
public void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName()+"賣出了第"+(number--)+"張禁荒,還剩"+number+"張。");
}
}
}
運(yùn)行結(jié)果:
A賣出了第30張角撞,還剩29張呛伴。
A賣出了第29張,還剩28張谒所。
A賣出了第28張热康,還剩27張。
A賣出了第27張劣领,還剩26張姐军。
A賣出了第26張,還剩25張尖淘。
A賣出了第25張奕锌,還剩24張。
A賣出了第24張村生,還剩23張惊暴。
A賣出了第23張,還剩22張趁桃。
A賣出了第22張辽话,還剩21張肄鸽。
A賣出了第21張,還剩20張油啤。
A賣出了第20張典徘,還剩19張。
A賣出了第19張益咬,還剩18張逮诲。
A賣出了第18張,還剩17張础废。
C賣出了第16張汛骂,還剩15張罕模。
C賣出了第15張评腺,還剩14張。
C賣出了第14張淑掌,還剩13張蒿讥。
C賣出了第13張,還剩12張抛腕。
C賣出了第12張芋绸,還剩11張。
C賣出了第11張担敌,還剩10張摔敛。
C賣出了第10張,還剩9張全封。
C賣出了第9張马昙,還剩8張。
C賣出了第8張刹悴,還剩7張行楞。
C賣出了第7張,還剩6張土匀。
C賣出了第6張子房,還剩5張。
C賣出了第5張就轧,還剩4張证杭。
C賣出了第4張,還剩3張妒御。
C賣出了第3張解愤,還剩2張。
C賣出了第2張携丁,還剩1張琢歇。
C賣出了第1張兰怠,還剩0張。
B賣出了第17張李茫,還剩16張揭保。
Process finished with exit code 0
很明顯可以看到,多個(gè)線程操作一個(gè)資源類的時(shí)候魄宏,會(huì)出現(xiàn)搶占的情況秸侣,導(dǎo)致運(yùn)行結(jié)果不是我們想要看到的那樣,所以為保證線程同步安全宠互,傳統(tǒng)方式我們會(huì)在資源類中加入Synchronized鎖以達(dá)到我們保證線程同步安全的目的味榛。
/**
* synchronized 本質(zhì):隊(duì)列、鎖*/
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName()+"賣出了第"+(number--)+"張予跌,還剩"+number+"張搏色。");
}
}
使用Synchronized鎖后的運(yùn)行結(jié)果:
A賣出了第30張,還剩29張券册。
A賣出了第29張频轿,還剩28張。
A賣出了第28張烁焙,還剩27張航邢。
A賣出了第27張,還剩26張骄蝇。
A賣出了第26張膳殷,還剩25張。
A賣出了第25張九火,還剩24張赚窃。
A賣出了第24張,還剩23張吃既。
A賣出了第23張考榨,還剩22張。
A賣出了第22張鹦倚,還剩21張河质。
A賣出了第21張,還剩20張震叙。
A賣出了第20張掀鹅,還剩19張。
A賣出了第19張媒楼,還剩18張乐尊。
A賣出了第18張,還剩17張划址。
A賣出了第17張扔嵌,還剩16張限府。
A賣出了第16張,還剩15張痢缎。
A賣出了第15張歧寺,還剩14張主儡。
A賣出了第14張挫酿,還剩13張宿稀。
A賣出了第13張,還剩12張嵌洼。
A賣出了第12張案疲,還剩11張。
A賣出了第11張麻养,還剩10張褐啡。
B賣出了第10張,還剩9張回溺。
B賣出了第9張春贸,還剩8張混萝。
B賣出了第8張遗遵,還剩7張。
B賣出了第7張逸嘀,還剩6張车要。
B賣出了第6張,還剩5張崭倘。
B賣出了第5張翼岁,還剩4張。
B賣出了第4張司光,還剩3張琅坡。
B賣出了第3張,還剩2張残家。
B賣出了第2張榆俺,還剩1張。
B賣出了第1張坞淮,還剩0張茴晋。
Process finished with exit code 0
Lock鎖
基本實(shí)現(xiàn)類,常用ReentrantLock可重入鎖
ReentrantLock有兩個(gè)構(gòu)造方法回窘,無參構(gòu)造默認(rèn)非公平鎖诺擅,可以允許插隊(duì);有參構(gòu)造可以傳入一個(gè)Boolean值進(jìn)行判斷啡直,若為true烁涌,則是公平鎖苍碟,不允許插隊(duì),先來的先執(zhí)行撮执!
使用Lock鎖的買票例子:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicket02 {
public static void main(String[] args) {
Ticket02 ticket = new Ticket02();
//并發(fā)驰怎,多個(gè)線程操作同一個(gè)資源類,將類丟入線程二打。
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 1; i < 40; i++){
ticket.sale();
}
},"C").start();
}
}
/**
* Lock三部曲
* 1.new ReentrantLock()
* 2.lock.lock() 加鎖;
* 3.finally ==> lock.unlock() 解鎖*/
class Ticket02{
/*票數(shù)*/
private int number = 30;
//Lock鎖
Lock lock = new ReentrantLock();
/*買票的方式*/
public void sale(){
//加鎖
lock.lock();
try {
//業(yè)務(wù)代碼
if (number > 0){
System.out.println(Thread.currentThread().getName()+"賣出了第"+(number--)+"張县忌,還剩"+number+"張。");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//解鎖
lock.unlock();
}
}
}
Synchronized與Lock的區(qū)別
- synchronized 是內(nèi)置的Java關(guān)鍵字继效,Lock 是一個(gè)Java類症杏。
- synchronized 無法判斷獲取鎖的狀態(tài),Lock 可以判斷是否獲取到了鎖瑞信。
- synchronized 會(huì)自動(dòng)釋放鎖厉颤,Lock 必須要手動(dòng)釋放鎖,若不釋放鎖凡简,會(huì)死鎖逼友!
- synchronized 線程A(獲得鎖,阻塞)秤涩,線程B(等待帜乞,一直等待);Lock 不一定會(huì)等待下去筐眷,可以用lock.tryLock()嘗試獲取鎖黎烈。
- synchronized 默認(rèn)可重入,不可以中斷的匀谣,非公平照棋;Lock 默認(rèn)可重入,可以進(jìn)行判斷鎖武翎,非公平(可以進(jìn)行設(shè)置)烈炭。
- synchronized 適合鎖少量的代碼同步問題;Lock 適合鎖大量的同步代碼宝恶!