多執行緒安全問題解決之顯示鎖Lock
阿新 • • 發佈:2018-12-10
【1】顯示鎖和隱式鎖
在Java 5.0 之前,協調共享物件的訪問時可以使用的機制只有synchronized 和volatile 。Java 5.0 後增加了一些新的機制,但並不是一種替代內建鎖的方法,而是當內建鎖不適用時,作為一種可選擇的高階功能。即,顯示鎖(同步鎖)-Lock。
使用者解決多執行緒安全問題的三種方式:
- synchronized同步程式碼塊;
- synchronized同步方法;
- 同步鎖Lock。
其中synchronized被稱之為隱式鎖,Lock被稱之為顯示鎖。
何為顯示鎖?
即需要通過lock()方法上鎖,且必須通過unlock()方法進行釋放鎖。 通常為了保證鎖的釋放,將unlock()方法放在finally中。
在java.util.concurrent.locks包下關於鎖與同步輔助類如下:
【2】多執行緒賣票問題與Lock例項
先來看一個多執行緒賣票問題:
public class TestLock {
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(ticket,"1號視窗").start();
new Thread(ticket,"2號視窗").start();
new Thread(ticket,"3號視窗" ).start();
}
}
class Ticket implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
try {
//放大多執行緒問題
Thread.sleep(200);
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 完成售票,餘票為:" +--ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測試結果如下圖所示:
這裡可能有同學會說,參考從記憶體可見性看Volatile,使用Volatile,其可以使共享資料在多個執行緒中保持記憶體可見性,並能夠將資料變動通知其他執行緒。
那麼同樣存在問題,正如那篇博文介紹的那樣,volatile不具有互斥性,不支援變數原子操作。
如下所示:
public class TestLock {
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(ticket,"1號視窗").start();
new Thread(ticket,"2號視窗").start();
new Thread(ticket,"3號視窗").start();
}
}
class Ticket implements Runnable{
//使用volatile修飾
private volatile int ticket = 100;
@Override
public void run() {
while (true){
try {
//放大多執行緒問題
Thread.sleep(200);
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 完成售票,餘票為:"+--ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測試如下圖:
不光資料不準備,甚至還有負數!
synchronized當然可以解決這個問題,不過這裡不使用synchronized,使用lock。
修改程式碼如下:
public class TestLock {
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(ticket,"1號視窗").start();
new Thread(ticket,"2號視窗").start();
new Thread(ticket,"3號視窗").start();
}
}
class Ticket implements Runnable{
private volatile int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
//放大多執行緒問題
Thread.sleep(200);
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 完成售票,餘票為:"+--ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//保證鎖的釋放
lock.unlock();
}
}
}
}
再次測試,結果正常!