1. 程式人生 > >多執行緒安全問題解決之顯示鎖Lock

多執行緒安全問題解決之顯示鎖Lock

【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();
            }
        }

    }
}

再次測試,結果正常!

這裡寫圖片描述