理解java的ReetrantLock__多線程

來源:互聯網
上載者:User

關於java多線程並發控制,除了內建關鍵字synchronized外,還有lock,lock的一個實作類別就是ReetrantLock,Lock可以實現更靈活的多線程並發控制。

如何使用,舉個栗子

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                }            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable2 running " + i);                }            } finally {                lock.unlock();            }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

這就是lock的最基本使用,使用lock和unlock方法對代碼片進行加鎖瞭解鎖,效果和synchronized一樣,兩個線程序列化執行了,控制台輸出如下:
runnable1 running 0
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable1 running 4
runnable2 running 0
runnable2 running 1
runnable2 running 2
runnable2 running 3
runnable2 running 4

也就是說synchronized能做的lock都能做,那麼看看lock還能做什麼。它還提供了trylock方法,這個方法就是說我會嘗試擷取鎖,但擷取鎖如果失敗,不會導致當前線程阻塞,直接跳過,把上面的栗子稍微改造一下。

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                }            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {            if (lock.tryLock()) {                try {                    for (int i = 0; i < 5; i++) {                        System.out.println("runnable2 running " + i);                    }                } finally {                    lock.unlock();                }            } else {                System.out.println("Can not get the lock, skip running");            }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

線上程2中嘗試擷取鎖,如果擷取不到,就直接跳過,控制台輸出如下:
runnable1 running 0
Can not get the lock, skip running
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable1 running 4

另外trylock方法還可以設定等待時間,等待多少時間後擷取不了鎖就跳過。在構造ReetrantLock時可以設定一個boolean值,就是 new ReetrantLock(true/false),選擇是否構造一個“公平鎖”,所謂公平鎖就是讓所有等待線程按他們的等待順序來得到當前鎖,防止一些線程很久都不被執行,不過需要注意實現公平鎖需要一些額外計算開銷,慎用。

lock還有一個功能就是condition,簡單的說就是條件設定。舉個栗子,比如上面2個線程,需求是第一個線程迴圈到3的時候第二個線程開始,這時候第一個線程停止,等到第二個線程執行到3的時候在啟動。那麼這裡就有2個條件:
1. 第二個線程需要在第一個線程執行到3的時候才能啟動,用condition1表示
2. 第一個線程需要在第二個線程執行到3的時候重啟。用condition2表示

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static boolean thread1Arrive3 = false;    static boolean thread2Arrive3 = false;    static final Condition condition1 = lock.newCondition();    static final Condition condition2 = lock.newCondition();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                    if(i==3){                        thread1Arrive3=true;                        condition1.signal();                        if(!thread2Arrive3){                            condition2.await();                        }                                   }                }            } catch (Exception e) {                e.printStackTrace();            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {                lock.lock();                try {                    if(!thread1Arrive3){                        condition1.await();                    }                    for (int i = 0; i < 5; i++) {                        System.out.println("runnable2 running " + i);                        if(i==3){                            thread2Arrive3=true;                            condition2.signal();                        }                    }                } catch (Exception e) {                    e.printStackTrace();                } finally {                    lock.unlock();                }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

運行結果:
runnable1 running 0
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable2 running 0
runnable2 running 1
runnable2 running 2
runnable2 running 3
runnable2 running 4
runnable1 running 4

condition的await方法可以讓當前線程釋放鎖並且在指定的condition上面進行等待,而signal方法則通知在condition上等待的線程擷取鎖並繼續執行。condition其實維護了一個隊列的資料結構,await相當於往隊列中put,而signal則是從隊列中取出。所以需要注意如果在執行signal時,沒有線程在這個condition上去await,singal方法也不會阻塞。所以需要注意使用condition的時候一定是在滿足條件的時候才去進行await和signal(範例程式碼中使用2個boolean變數作為判定條件),如果signal先執行,await後執行就直接死結了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.