synchronized 和 lock 基本使用以及死鎖分析
阿新 • • 發佈:2019-02-17
1.未加鎖
多執行緒程式未加鎖可能產生安全問題
執行緒未加鎖可能造成資料重複訪問
如下程式
public class SellTickets implements Runnable{
private int ticketNumber = 100;
@Override
public void run() {
while(true){
if(ticketNumber > 0){
System.out.println(Thread.currentThread().getName()+"正在出售第" + ticketNumber-- +"張票");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SellTickets seller = new SellTickets();
Thread seller1 = new Thread(seller,"售票員1");
Thread seller2 = new Thread(seller,"售票員2");
Thread seller3 = new Thread(seller,"售票員3");
seller1.start();
seller2.start();
seller3.start();
}
}
Console
2.使用synchronized加鎖
synchronized特性:
一 : 採用synchronized修飾符實現的同步機制叫做互斥鎖機制,它所獲得的鎖叫做互斥鎖。每個物件的鎖只能分配給一個執行緒,因此叫做互斥鎖
二 :類的每個例項都有自己的物件級別鎖。當一個執行緒訪問例項物件中的synchronized同步程式碼塊或同步方法時,該執行緒便獲取了該例項的物件級別鎖,其他執行緒這時如果要訪問synchronized同步程式碼塊或同步方法,便需要阻塞等待,直到前面的執行緒從同步程式碼塊或方法中退出,釋放掉了該物件級別鎖
三 :訪問同一個類的不同例項物件中的同步程式碼塊,不存在阻塞等待獲取物件鎖的問題,因為它們獲取的是各自例項的物件級別鎖,相互之間沒有影響。
為synchronized指定程式碼塊加鎖(該程式碼塊一個執行緒訪問時,其他執行緒無法同時訪問此塊程式碼)
synchronized ((同步物件一般用)this) {
//指定程式碼塊
//鎖物件可以是任意物件(但須確保為同一物件才能保證資料加鎖)
}
synchronized修飾方法,則該方法內所有程式碼加鎖
若synchronized需加鎖為靜態方法一般採用則使用類名.class作為引數
3.使用Lock加鎖
lock特徵:
Lock實現提供比使用synchronized方法和語句可以獲得的更廣泛的鎖定操作。 它們允許更靈活的結構化,可能具有完全不同的屬性,並且可以支援多個相關聯的物件Condition 。
所有已知實現類:
ReentrantLock( 重入鎖), ReentrantReadWriteLock.ReadLock (讀鎖), ReentrantReadWriteLock.WriteLock (寫鎖)
本文將使用ReentrantLock實現類來為執行緒加鎖,需要注意,鎖物件解鎖需要在finally語句塊中,
ReentrantLock lock = new ReentrantLock();
while(true){
try{
lock.lock();
if(ticketNumber > 0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+ ticketNumber-- +"張票");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally{
lock.unlock();
}
}
4.死鎖問題
如果出現了同步巢狀,就容易產生死鎖問題
是指兩個或者兩個以上的執行緒在執行的過程中,因爭奪資源產生的一種互相等待現象
可以理解為A執行緒訪問某B執行緒正在訪問的物件過程中,B執行緒同時訪問A所訪問的物件,相互加鎖,無法正常執行
同步程式碼塊的巢狀案例
public class DieLockDemo extends Thread{
//建立兩把鎖物件
public static final Object obj1 = new Object();
public static final Object obj2 = new Object();
private boolean flag = false;
public DieLockDemo(String name ,boolean flag) {
super(name);
this.flag = flag;
}
@Override
public void run() {
while(true){
if(flag){
synchronized (obj1) {
System.out.println(getName()+" use obj1");
synchronized (obj2) {
System.out.println(getName()+" use obj2");
}
}
} else {
synchronized (obj2) {
System.out.println(getName()+" use obj1");
synchronized (obj1) {
System.out.println(getName()+" use obj2");
}
}
}
}
}
public static void main(String[] args) {
DieLockDemo d1 = new DieLockDemo("小王", true);
DieLockDemo d2 = new DieLockDemo("小強", false);
d1.start();
d2.start();
}
}