1. 程式人生 > >Java並發編程:鎖的釋放

Java並發編程:鎖的釋放

author emacs cde pri ebe mage 方法 rest orm

Java並發編程:鎖的釋放

Table of Contents

  • 1. 線程的狀態
  • 2. wait() notify() 和 notifyAll()

上一篇線程的同步,我們講了鎖的獲得方式。接下來,我們講講鎖的釋放。首先,鎖定的方法或者代碼塊運行完畢,肯定會釋放鎖。
那麽,主動釋放鎖,是通過什麽方法來達到的呢?
如果,我們看過Object類的方法,我們會註意到它有三個奇怪的方法:wait(),notify()和notifyAll()。 它們就是用來釋放鎖的。

1 線程的狀態

在講鎖的釋放前,我們先講一下線程的三種主要狀態:運行、就緒(可運行)、阻塞。當然,除了這個之外,肯定還有初始狀態和結束狀態,那個就不討論了。

技術分享

當我們創建線程之後,還只是進入初始狀態,如果我們調用run()方法運行,根本就不會啟動新的線程。當調用start()後,可以將線程狀態改為可運行狀態,然後,由操作系統來決定調用哪個線程。當幸運的被操作系統選中之後,就可以進入真正的運行狀態了。當運行當時間片用完,或者調用yield()禮讓方法,就會把當前當執行權交出來,進入可運行就緒狀態。如果在運行的過程中,有系統IO,如等待輸入,彈出確認對話框等,則會使當前線程進入阻塞狀態,動彈不得。只有等待用戶操作之後,才能往下進行。sleep()方法和join()方法可以可以達到類似的阻塞效果。

技術分享
然後,我們把對象鎖部分的狀態也加進來。當我們使用synchronized修飾方法或者代碼塊時,會獲取對象的鎖,並進入獲取該對象鎖的等待隊列。當獲取到該對象鎖之後,就可以進入可運行狀態了。
另外,還有一個對象的wait()方法,可以使線程放棄持有的該對象鎖,並進入通知等待狀態。當其他線程調用等待線程需要的對象的notify()或者notifyAll()方法時,該線程重新進入獲取對象鎖的隊列中參與鎖的獲取。

2 wait() notify() 和 notifyAll()

synchronized獲取鎖的方法我們已經詳細的講解過了,我們接下來來看一下如何主動釋放鎖。假設,我們想創建兩個線程,讓這兩個線程,依次做一件事情,而不是同時啟動之後,就由操作系統來決定它們的執行順序,那麽,我們該怎麽做呢?
那就是首先請求前一個線程的鎖,然後,獲取自己線程的鎖,再釋放自己的鎖,並通知這個鎖對象的等待隊列(下一個線程!哎哎,醒醒別睡啦,起來幹活啦!),釋放前一個線程的鎖,並進入等待前一個鎖對象的通知隊列。

技術分享

用代碼展示為:

public class SyncDemo implements Runnable {
    private String name;
    private Object prev;
    private Object cur;

    public SyncDemo(String name, Object prev, Object cur) {
        this.name = name;
        this.prev = prev;
        this.cur = cur;
    }

    @Override
    public void run() {
        int count = 10;
        while (count > 0) {
            synchronized(prev) {
                synchronized(cur) {
                    System.out.println(name);
                    count--;
                    cur.notify();
                }
                try {
                    prev.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();
        SyncDemo r1 = new SyncDemo("A", b, a);
        SyncDemo r2 = new SyncDemo("B", a, b);

        new Thread(r1).start();
        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(r2).start();
    }
}

寫得更通用一點,支持更多的線程依次執行:

public static void main(String[] args) {
    int num = 5;

    Object[] locks = new Object[num];
    String[] names = {"A", "B", "C", "D", "E"};
    SyncDemo[] runnable = new SyncDemo[num];

    for (int i = 0; i < num; i++) {
        locks[i] = new Object();
    }

    for (int i = 0; i < num; i++) {
        runnable[i] = new SyncDemo(names[i], locks[(i - 1 + num) % num] , locks[i]);
    }

    for (int i = 0; i < num; i++) {
        new Thread(runnable[i]).start();
        try {
            Thread.sleep(100);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Date: 2017-07-05 22:31

Author: WEN YANG

Created: 2017-07-06 Thu 23:14

Emacs 25.2.1 (Org mode 8.2.10)

Validate

Java並發編程:鎖的釋放