傳統(tǒng)的Synchronized鎖
線程就是一個單獨的資源類,它沒有任何的附屬操作!
先看不加Synchronized多線程并發(fā)下的買票問題:
public class SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//并發(fā),多個線程操作同一個資源類,將類丟入線程。
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+"張。");
}
}
}
運行結果:
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
很明顯可以看到,多個線程操作一個資源類的時候,會出現(xiàn)搶占的情況,導致運行結果不是我們想要看到的那樣,所以為保證線程同步安全,傳統(tǒng)方式我們會在資源類中加入Synchronized鎖以達到我們保證線程同步安全的目的。
/**
* synchronized 本質(zhì):隊列、鎖*/
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName()+"賣出了第"+(number--)+"張,還剩"+number+"張。");
}
}
使用Synchronized鎖后的運行結果:
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鎖

基本實現(xiàn)類,常用ReentrantLock可重入鎖

ReentrantLock有兩個構造方法,無參構造默認非公平鎖,可以允許插隊;有參構造可以傳入一個Boolean值進行判斷,若為true,則是公平鎖,不允許插隊,先來的先執(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ā),多個線程操作同一個資源類,將類丟入線程。
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è)務代碼
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關鍵字,Lock 是一個Java類。
- synchronized 無法判斷獲取鎖的狀態(tài),Lock 可以判斷是否獲取到了鎖。
- synchronized 會自動釋放鎖,Lock 必須要手動釋放鎖,若不釋放鎖,會死鎖!
- synchronized 線程A(獲得鎖,阻塞),線程B(等待,一直等待);Lock 不一定會等待下去,可以用lock.tryLock()嘗試獲取鎖。
- synchronized 默認可重入,不可以中斷的,非公平;Lock 默認可重入,可以進行判斷鎖,非公平(可以進行設置)。
- synchronized 適合鎖少量的代碼同步問題;Lock 適合鎖大量的同步代碼!