Java5執行緒併發庫之LOCK(鎖)&CONDITION(條件)實現執行緒同步通訊

Java5執行緒併發庫之LOCK(鎖)&CONDITION(條件)實現執行緒同步通訊
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

Lock(鎖)&Condition(條件)實現執行緒同步通訊

接下來介紹,java5執行緒併發庫裡面的鎖。跟鎖有關的類和介面主要是位於java.util.concurrent.locks包。

Lock(鎖)介面實現執行緒間互斥

下面我們先來了解Lock(鎖)這個介面,這個鎖的作用呢就非常類實於我們傳統模型執行緒中的synchronized關鍵字,它的作用主要用於執行緒互斥。就好比有一個人進了一個廁所,把那個廁所裡面的門給鎖上,那別人就進不去了,他出來的時候把那把鎖拿掉,別人就可以進去了。每個人進去的時候都可以上鎖,上鎖了,別人就進不去了。它相比這個synchronized來說呢,它更加物件導向。鎖本身就是一個物件,鎖上進不去,開啟,別人可以進去。這樣就更物件導向。下面我們通過程式碼來跟synchronized關鍵字對比一下看這個鎖怎麼用。

ReentrantLock類實現可重入的互斥鎖

Lock是一個介面,當我們想要獲得一個它的物件的時候就要new一個它的實現了類,這裡我們new ReentrantLock();這是一個可重入的互斥鎖 Lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。這樣我們就製造了一把鎖,當A執行緒來呼叫的時候,我們就在要執行緒互斥的程式碼的前面呼叫lock.lock();上一把鎖。就是A執行緒一進去以後,馬上就把鎖鎖上。當B執行緒再來的時候呢,能不能進呢,那就看B執行緒是不是也想要這把鎖,也必須用這把鎖,如果B執行緒用的是另外一把鎖,B執行緒是可以進的,只有B執行緒也要搶這一把鎖的時候,它搶不到了。當A執行緒執行完了要執行緒互斥的這部分程式碼以後呢,我們呼叫lock.unlock();方法,把鎖交回去。這樣兩個執行緒就不會打架了。但是這個時候還有一個問題,如果在被鎖上的這部分程式碼內部拋了異常,那麼整個方法就跳出了,程式碼不會向下執行,這個時候這個鎖還沒有釋放。就好比一個人進去了,好傢伙,他暈倒在裡面了,這時候誰也進不去了,所以,在這種情況下,我們最好是把這部分被鎖包裹的程式碼try起來,在finally程式碼塊裡面釋放鎖。就是以防萬一,就是說無論如何,不管是正常還是非正常的退出,我們都應該把鎖給釋放。這樣就保證別人也可以進去,即使之前哪個執行緒在拿到鎖之後死掉了,別人還是可以進去。否則就麻煩了。

public class LockTest {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

        new LockTest().init();

    }

 

    private void init() {

        final Outputer outputer = new Outputer();

        new Thread(new Runnable() {

            @Override

            public void run() {

                while (true) {

                    try {

                        Thread.sleep(10);

                    } catch (InterruptedException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                    outputer.output(“zhangxiaoxiang”);

                }

 

            }

        }).start();

 

        new Thread(new Runnable() {

            @Override

            public void run() {

                while (true) {

                    try {

                        Thread.sleep(10);

                    } catch (InterruptedException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                    outputer.output(“lihuoming”);

                }

 

            }

        }).start();

 

    }

 

    static class Outputer {

        Lock lock = new ReentrantLock();

 

        public void output(String name) {

            int len = name.length();

            lock.lock();

            try {

                for (int i = 0; i < len; i ) {

                    System.out.print(name.charAt(i));

                }

                System.out.println();

            } finally {

                lock.unlock();

            }

        }

    }

}

static class Outputer {

 

        public void output(String name) {

            int len = name.length();

            synchronized (Outputer.class) {

                for (int i = 0; i < len; i ) {

                    System.out.print(name.charAt(i));

                }

                System.out.println();

            }

        }

 

讀寫鎖ReadWriteLock介面和實現類ReentrantReadWriteLock可重入讀寫鎖

接下來我們來看,java5中的鎖比傳統執行緒模型中的synchronized關鍵字更強。它還可以實現讀寫鎖ReadWriteLock介面(用的是鎖分離的優化思想:只要兩個執行緒的操作互不影響,鎖就可以分離)。什麼叫讀寫鎖呢?就是多個讀鎖不互斥,而讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。這在我們實際專案當中,我們是經常需要這個功能的。我們為什麼要上鎖呀,就是為了保證資料的完整性。如果才讀了一半,你就跑進去寫,那資料改了,我都會來的就亂了。如果大家都在讀,如果想在這個資料是3,現在100個人在讀,請問有沒有問題?肯定沒有問題,怎麼讀都是3。就是怕這個讀的過程中,別人跑去改了,如果全是讀,是沒有問題的。如果大家全是讀的時候,大家還上鎖,好不好?不好,效能低,沒有併發了。我們希望可以提高效能,大家可以同時讀,但是又不打架。什麼時候會大家呢,就在於寫資料的時候容易打架,在修改資料的時候才打架。在讀的時候,又跑進去寫,容易出問題。在寫的時候還沒寫完,又跑進去寫,容易出問題。下面我們就寫一個讀寫鎖的應用。我們產生3個執行緒負責讀資料,還產生3個執行緒負責寫資料,如果我們不上讀寫鎖,我們會看到讀和寫交替的執行。就讀中有寫,寫中有讀。如果我們上了讀寫鎖,我們將看到什麼效果呢?讀中有讀,但是讀中沒有寫,寫中沒有讀,寫中也沒有寫。讀寫鎖非常重要,能夠提高效能,又能夠互斥。這個在java5以前幹不了這個事,現在java5幫我們提供了這種功能。非常好。

public class ReadWriteLockTest {

    public static void main(String[] args) {

        final Queue3 q3 = new Queue3();

        for (int i = 0; i < 3; i ) {

            new Thread() {

                public void run() {

                    while (true) {

                        q3.get();

                    }

                }

 

            }.start();

 

            new Thread() {

                public void run() {

                    while (true) {

                        q3.put(new Random().nextInt(10000));

                    }

                }

 

            }.start();

        }

 

    }

}

 

class Queue3 {

    private Object data = null;// 共享資料,只能有一個執行緒能寫該資料,但可以有多個執行緒同時讀該資料。

 

    ReadWriteLock rwl = new ReentrantReadWriteLock();

 

    public void get() {

        rwl.readLock().lock();

        try {

            System.out.println(Thread.currentThread().getName() ” be ready to read data!”);

            Thread.sleep((long) (Math.random() * 1000));

            System.out.println(Thread.currentThread().getName() “have read data :” data);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            rwl.readLock().unlock();

        }

    }

 

    public void put(Object data) {

 

        rwl.writeLock().lock();

        try {

            System.out.println(Thread.currentThread().getName() ” be ready to write data!”);

            Thread.sleep((long) (Math.random() * 1000));

            this.data = data;

            System.out.println(Thread.currentThread().getName() ” have write data: ” data);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            rwl.writeLock().unlock();

        }

 

    }

}

下面我們來看一個讀寫鎖的比較好的例子。

先簡單說一下Hibernate中session的load和get方法的區別:

get方法是直接去資料庫裡面查,如果有就返回,沒有就返回null.

load方法得到的是一個代理物件,這個代理物件裡面封裝了我們想要的真實的物件的引用和它的id作為成員欄位。他不會先去查資料庫,首先會把方法引數中傳入的id賦值給自己的id欄位。然後返回這個代理物件。當我們要呼叫方法獲取物件中id之外的欄位的時候,它會先判斷成員欄位,就是那個真實物件的引用是否為空,如果為空的話,他就呼叫get方法去查詢資料庫,如果查到就賦值給那個真實物件的引用變數,並返回,沒查到就丟擲異常。

好了,明白上面的思想,我們再來看下面的例子。

class CachedData {

    Object data;

 

    volatile boolean cacheValid;

 

    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

 

    void processCachedData() {//處理快取資料

     rwl.readLock().lock();[z1] 

     if (!cacheValid) {//檢查有沒有快取的資料

        // 在獲得寫鎖之前必須釋放讀鎖

        rwl.readLock().unlock();[z2] 

        rwl.writeLock().lock();[z3] 

        // 重新檢查狀態,因為另一個執行緒可能已經獲得寫入鎖定並在我們完成之前改變了狀態。

        if (!cacheValid) {//如果沒有快取資料

          data = …//去獲取資料

          cacheValid = true;//獲取資料之後把快取檢查值設為true

        }

        // 通過在釋放寫鎖之前獲取讀鎖來降級

        rwl.readLock().lock();[z4] 

        rwl.writeLock().unlock(); // 解鎖寫入,仍然保持讀取

     }

 

     use(data);//使用資料

     rwl.readLock().unlock();

   }

}

以上程式碼,其實就是,大家都去讀資料,然後又怕某一個執行緒在寫資料,所以大家一開始都掛了讀鎖,但是發現沒有資料可讀呢,第一個傢伙發現沒有資料可讀呢,他馬上掛一把寫鎖,就讓別人讀的暫時先不要進來了,等著,因為現在沒有資料,我把資料寫完了,我再把寫鎖解放,我也掛讀鎖,恢復成為讀鎖,大家就又都可以讀了。這個例子就是一個快取,一個代理的快取,一個實體的快取,不是快取系統。

下面我們再做一個思考題,請你寫出一個快取系統的虛擬碼。這道題目跟我們上面講的快取代理物件不一樣,快取系統就是說可以裝很多個物件,我們上面的快取代理物件只能夠裝一個物件。什麼是快取呢?就是人家要取資料,別人呼叫我,呼叫我的一個方法叫getData(),我就應該檢查,我內部是否有這個資料,如果有這個資料,我就直接給人家,如果沒有這個資料,我就去資料庫裡面查,我查到以後,我就把查到的資料存到我的記憶體裡面來,下次再有別人再找我要這個資料,就直接返回給人家,就不需要再查資料庫了。這就是快取系統的概念。你要拿物件不要直接找資料庫,找我。如果我內部沒有,我去查資料庫,給你。和你直接查資料庫是一樣的。好處就在於,你下次再來找我的時候,我就不再查資料庫了,我直接給你。這就是Cache.

因為我們是定義自己的Cache,我要存一堆物件,那如果人家來找我取某個物件的時候,怎麼來取呢?我們定義一個key(key存的就是物件的名字),讓要取物件的人把key給我,我就把這個key對應的物件給他。所以在快取物件的內部,我們要定義一個Map成員,用來有對應規則的儲存物件。那麼現在人家來找我取資料,我是怎麼做呢?我就先到map裡面去get(key),來得到一個物件,如果這個物件為null,我就去查資料庫,查到以後我再返回這個物件,在返回之間,我現在自己的map裡面存一份。這就是一個簡單的快取系統。

public class CacheDemo {

 

    private Map<String, Object> cache = new HashMap<String, Object>();

 

    public static void main(String[] args) {

        // TODO Auto-generated method stub

 

    }

 

    private ReadWriteLock rwl = new ReentrantReadWriteLock();

 

    public Object getData(String key) {

        rwl.readLock().lock();

        Object value = null;

        try {

            value = cache.get(key);

            if (value == null) {

                rwl.readLock().unlock();

                rwl.writeLock().lock();

                try {

                    if (value == null) {

                        value = “aaaa”;// 實際是去queryDB();

                    }

                } finally {

                    rwl.writeLock().unlock();

                }

                rwl.readLock().lock();

            }

        } finally {

            rwl.readLock().unlock();

        }

        return value;

    }

}

Condition實現互斥執行緒間的通訊

接下來我們來介紹java5併發庫這個java.util.concurrent.locks子包裡面的另一個東西,叫Condition,叫條件。這個Condition到底是什麼呢?是執行條件,就類似與我們傳統執行緒同步模型中的Object的wait()方法和notify()方法。我們說,鎖,只能實現互斥,就是說,我進去了,你不能進來。但是無法實現通訊。什麼叫通訊呢?說,哥們,你可以執行了。這就是通訊。Condition就是用來解決這個問題的。就是說CPU即使分給了我,我進來了,但是,我可以讓出CPU,說,兄弟,你幹吧。我暫停,兄弟你幹完了,再通知我,說,大哥,謝謝你,你再幹吧。這就是通訊。下面我們來看一個Condition實現執行緒通訊的程式碼例子,對比Object的wait()和notify();注意:Condition是基於鎖Lock之上的。Condition一定是在某個lock物件基礎上,也就是說,是基於某個Lock物件以後,在lock物件下面new出一個Condition(呼叫lock.newCondition();方法)。

public class ConditionCommunication {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

 

        final Business business = new Business();

        new Thread(new Runnable() {

 

            @Override

            public void run() {

 

                for (int i = 1; i <= 50; i ) {

                    business.sub(i);

                }

 

            }

        }).start();

 

        for (int i = 1; i <= 50; i ) {

            business.main(i);

        }

 

    }

 

    static class Business {

        Lock lock = new ReentrantLock();

 

        Condition condition = lock.newCondition();

 

        private boolean bShouldSub = true;

 

        public void sub(int i) {

            lock.lock();

            try {

                while (!bShouldSub) {

                    try {

                        condition.await();

                    } catch (Exception e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                for (int j = 1; j <= 10; j ) {

                    System.out.println(“sub thread sequence of ” j “,loop of ” i);

                }

                bShouldSub = false;

                condition.signal();

            } finally {

                lock.unlock();

            }

        }

 

        public void main(int i) {

            lock.lock();

            try {

                while (bShouldSub) {

                    try {

                        condition.await();

                    } catch (Exception e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                for (int j = 1; j <= 100; j ) {

                    System.out.println(“main thread sequence of ” j “,loop of ” i);

                }

                bShouldSub = true;

                condition.signal();

            } finally {

                lock.unlock();

            }

        }

 

    }

}

class Business {

    private boolean bShouldSub = true;

    public synchronized void sub(int i){

        while(!bShouldSub){

            try {

                this.wait();

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

        for(int j=1;j<=10;j ){

            System.out.println(“sub thread sequence of ” j “,loop of ” i);

        }

        bShouldSub = false;

        this.notify();

    }

 

    public synchronized void main(int i){

        while(bShouldSub){

            try {

                this.wait();

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

        for(int j=1;j<=100;j ){

            System.out.println(“main thread sequence of ” j “,loop of ” i);

        }

        bShouldSub = true;

        this.notify();

    }

}

 

使用Condition的好處,它可以有多路等待情況,它可以實現我們用Object的wait()和notify()這兩個方法實現不了的功能。下面我們看一個Condition實現多路等待的例子:

這是一個佇列,可阻塞的一個佇列的底層程式碼實現。

我們先來了解一下什麼叫阻塞佇列:有一個發射機,對外發射訊號。這個發射機發射的型號來自於哪裡呢?來自於一個共同機,也就是一個電腦吧。電腦它連著發射機,電腦裡面有很多的簡訊,都是通過發射機來發射出去的。那麼,這個電腦又是怎麼收到這個簡訊的呢?通過尋呼臺的坐席接聽客戶的電話,然後講客戶要傳送的資訊通過網路輸入到電腦。有很多個坐席,電腦收到了很多個留言,它就把這些留言交給發射機一個一個去發,這個電腦系統內部的程式碼怎麼寫?現在這個發射機對外,1秒鐘可以發射一個簡訊,現在這1秒中正好來了一句,欸,這個發射機就發射出去了。如果這個一秒鐘來了兩個,這個發射機發射幾個?發一個。還有一個就沒了,你叫我,我是不是正在處理你呀,另外一個人也扔給我一個包,我沒有時間去收它,那怎麼辦呢?我們準備一個佇列,說白了就是一個陣列,然後人家給我一個呼,我就往佇列裡面放,再有一個坐席給我一個呼,我又往佇列裡面按順序放。依次這樣操作。我有兩個執行緒,一個執行緒是專門對應這些坐席,把他們的尋呼資訊收進佇列,我還有一個執行緒是專門從這個佇列裡面取,取出來交給發射機。這個佇列總共有8個格子0-7,我取的話是從第0個格子開始取,然後依次往上增,這時候如果我已經取到了序號為7的格子,取完了,接著,我又開始從序號為0的格子開始取,這時候就要有一個變數,這個變數記錄了正要取得那個格子得序號。接下來我放的時候,剛開始我放的是第0個格子,然後這樣依次往上遞增,當放到序號為7的格子之後,又從新從0號格子開始放,假設這個發射機還沒有把這個第0個格子的資訊取走,我怎麼辦呢?我就阻塞等在那裡,等到他一取走,我馬上就放進去。這就是阻塞,就是不能繼續往裡面放了,我沒地方放了,我在外面等著。暫停,等待發射機發一個,空一個格子出來,空一個格子出來以後,我就可以放進去了。他一空出來,我馬上就放進去,這是系統自動幫我們排程的。我們只要用到相應的技術就行,至於它內部怎麼實現,我們並不管。現在,作業系統應該給我們提供解決這種問題的技術。

因為我現在佇列已經滿了,沒有富餘的空間放了。取的時候有沒有一種可能,假如我已經放了3個,現在我取到3了,我再接著要取,是不是要取第四個?第四個你放資料沒有?你要沒放,那時候我怎麼辦?阻塞,在那裡等,一旦你放了一個進來,是不是你正好就放在第四個格子了?我就把它取走了。那我現在為什麼要用這個佇列呢?起到緩衝的效果。不是快取,是緩衝。緩衝區。我們是平均每一秒中發一個簡訊,我們也計算過,我們的客戶有3000萬,我們是平均每一秒鐘收一個簡訊,所以是不是正好,我一個發射機就夠呀!平均每一秒鐘收一個,那就意味著有時1秒鐘收6個,有時候3秒鐘內一個都沒收到。但是平均下來,1秒鐘一個。這時候進入這個緩衝區,你這一秒鐘來了3個,沒關係,我放在這個佇列裡面,下一秒鐘沒有了,那我在緩衝佇列裡面取,慢慢發。這就是緩衝區的作用。如果說來了1萬個,但我現在緩衝區只有8個格子,那就有1萬-8的人在那裡阻塞。如果出現這種情況,系統會不會崩?就算不崩,你公司也要崩了。這時候我趕快擴容唄。剛開搞更多的發射機唄。這個什麼東西都是有個度的,你只是解決少量的這個問題,如果太大量,這個時候要解決硬體問題了,別指望這個軟體崩不崩了。

那下面看,人家給我們實現了這麼一個可阻塞的佇列。佇列,先放進來的先取走。

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

 

class BoundedBuffer {

    final Lock lock = new ReentrantLock();

 

    final Condition notFull = lock.newCondition();

 

    final Condition notEmpty = lock.newCondition();

 

    final Object[] items = new Object[100];//佇列裡面1最多裝100個物件

 

    int putptr, takeptr, count;

 

    public void put(Object x) throws InterruptedException {//往佇列裡面放資料

        lock.lock();

        try {

            while (count == items.length)//假如格子放滿了,就等待

                notFull.await();

            items[putptr] = x;//放到陣列裡面的哪個索引

            if ( putptr == items.length)//這個指標指示資料存放到陣列的哪個索引

                putptr = 0;

            count;//每放一個就把count給 ,count記錄實際有效的內容有幾個

            notEmpty.signal();

        } finally {

            lock.unlock();

        }

    }

 

    public Object take() throws InterruptedException {//從佇列裡取資料

        lock.lock();

        try {

            while (count == 0)//假如佇列裡面一個資料都沒有,就等待

                notEmpty.await();

            Object x = items[takeptr];

            if ( takeptr == items.length)

                takeptr = 0;

            –count;//每取走一個就把count給–

            notFull.signal();

            return x;

        } finally {

            lock.unlock();

        }

    }

}

用一個Condition也能實現功能,為什麼程式碼裡面要用兩個Condition?既然它用了兩個,這裡面一定是有原因的。如果我們只用一個Condition到底會出現什麼問題?假如我現在有5個執行緒同時在往緩衝區裡面放資料,發現緩衝區已經滿了,這時,這五個執行緒都在放資料的方法阻塞。結果,這時有一個執行緒去取資料,取完了資料就通知其它執行緒中的一個喚醒。這五個等待放資料的執行緒中有一個被喚醒了,喚醒了它就去放資料,放完資料之後,他又發一個通知去喚醒正在等待的執行緒中的某個甦醒,那麼現在問題來了,他本來是要通知取的執行緒,說,我已經放完了,你現在可以取了。但是由於放資料和取資料用的是同一個Condition通訊,此時等待通知的執行緒裡面除了那個取資料的執行緒,還有4個放資料的執行緒,結果它這個喚醒通知沒有通知到取資料的那個哥們,而是同時到了放資料的4個裡面的一個,其實剛才他放資料的時候就又把緩衝區給放滿了,此時這個被喚醒的放資料的執行緒一看緩衝區是滿的,只好又繼續阻塞等待。所以,如果有兩個Condition的話們就可以把放資料和取資料的執行緒區分開來,一個只用來喚醒放資料的執行緒放資料,一個只用來喚醒取資料的執行緒取資料。通過這樣,我們就自圓其說了。

根據我們上面的理解,下面我們來寫一個3個Condition通訊的程式碼:

public class ThreeConditionCommunication {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

 

        final Business business = new Business();

        new Thread(new Runnable() {

 

            @Override

            public void run() {

 

                for (int i = 1; i <= 50; i ) {

                    business.sub2(i);

                }

 

            }

        }).start();

 

        new Thread(new Runnable() {

 

            @Override

            public void run() {

 

                for (int i = 1; i <= 50; i ) {

                    business.sub3(i);

                }

 

            }

        }).start();

 

        for (int i = 1; i <= 50; i ) {

            business.main(i);

        }

 

    }

 

    static class Business {

        Lock lock = new ReentrantLock();

        //規則,main喚醒sub2,sub2喚醒shub3,sub3喚醒main。所以我們定義3個condition各司其職。

        Condition condition1 = lock.newCondition();//用於控制main的condition

 

        Condition condition2 = lock.newCondition();//用於控制sub2的condition

 

        Condition condition3 = lock.newCondition();//用於控制sub3的condition

 

        private int shouldSub = 1;

 

        public void sub2(int i) {

            lock.lock();

            try {

                while (shouldSub != 2) {

                    try {

                        condition2.await();

                    } catch (Exception e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                for (int j = 1; j <= 10; j ) {

                    System.out.println(“sub2 thread sequence of ” j “,loop of ” i);

                }

                shouldSub = 3;

                condition3.signal();

            } finally {

                lock.unlock();

            }

        }

 

        public void sub3(int i) {

            lock.lock();

            try {

                while (shouldSub != 3) {

                    try {

                        condition3.await();

                    } catch (Exception e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                for (int j = 1; j <= 20; j ) {

                    System.out.println(“sub3 thread sequence of ” j “,loop of ” i);

                }

                shouldSub = 1;

                condition1.signal();

            } finally {

                lock.unlock();

            }

        }

 

        public void main(int i) {

            lock.lock();

            try {

                while (shouldSub != 1) {

                    try {

                        condition1.await();

                    } catch (Exception e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                }

                for (int j = 1; j <= 100; j ) {

                    System.out.println(“main thread sequence of ” j “,loop of ” i);

                }

                shouldSub = 2;

                condition2.signal();

            } finally {

                lock.unlock();

            }

        }

 

    }

}

 [z1]執行緒進來是來讀資料的,所以,先拿了一個讀鎖鎖上,這個讀鎖它並不排斥其它的執行緒來拿,大家可以同時拿讀鎖。

 [z2]這個時候,上面的if語句檢查到並沒有資料可讀,所以接下來,這個執行緒準備進行寫操作,這時候它釋放了之前拿到的讀鎖,準備去拿寫鎖。

 [z3]執行緒拿了一把寫鎖,寫鎖是互斥的,同一個寫鎖同一時間只能有一個執行緒持有。

 [z4]注意這段程式碼:它掛上了一把讀鎖準備去讀資料,在讀資料之前,它要先把之前持有的寫鎖釋放掉。我們的疑惑是,寫鎖裡面是不能有讀鎖的,因為寫的時候是不允許讀的。解釋:我自己掛的寫鎖,我還是可以掛讀鎖的,也就說,當一個執行緒拿到了寫鎖之後,這個執行緒還可以繼續去拿讀鎖。鎖是用來跟別的執行緒互斥的,是用來擋別人的,不是用來擋自己的。它其實是這樣的,我在釋放寫鎖前去掛讀鎖,就會把寫鎖降級,降級成為更新鎖。更新鎖只有當前執行緒才可以掛,別人掛不上的。


(adsbygoogle = window.adsbygoogle || []).push({});

function googleAdJSAtOnload() {
var element = document.createElement(“script”);
element.src = “//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js”;
element.async = true;
document.body.appendChild(element);
}
if (window.addEventListener) {
window.addEventListener(“load”, googleAdJSAtOnload, false);
} else if (window.attachEvent) {
window.attachEvent(“onload”, googleAdJSAtOnload);
} else {
window.onload = googleAdJSAtOnload;
}

程式語言 最新文章