sleep() wait() yield() join()傻傻風不清楚?

功能

sleep()

sleep()方法需要指定等待的時間,它可以讓當前正在執行的線程在指定的時間內暫停執行,進入阻塞狀態。可以讓其他同優先級或者高優先級的線程得到執行的機會,也可以讓低優先級的線程得到執行機會。但是sleep()方法不會釋放“鎖標誌”,也就是說如果有synchronized同步塊,其他線程仍然不能訪問共享數據。

wait()

  wait()方法需要和notify()及notifyAll()兩個方法一起介紹,這三個方法用於協調多個線程對共享數據的存取,所以必須在synchronized語句塊內使用,也就是說,調用wait(),notify()和notifyAll()的任務在調用這些方法前必須擁有對象的鎖。注意,它們都是Object類的方法,而不是Thread類的方法。   除了使用notify()和notifyAll()方法,還可以使用帶毫秒參數的wait(long timeout)方法,效果是在延遲timeout毫秒後,被暫停的線程將被恢復到鎖標誌等待池。   wait(),notify()及notifyAll()只能在synchronized語句中使用,但是如果使用的是ReenTrantLock實現同步,該如何達到這三個方法的效果呢?解決方法是使用ReenTrantLock.newCondition()獲取一個Condition類對象,然後Condition的await(),signal()以及signalAll()分別對應上面的三個方法。

yield()

  yield()方法和sleep()方法類似,也不會釋放“鎖標誌”,區別在於,它沒有參數,即yield()方法只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行,另外yield()方法只能使同優先級或者高優先級的線程得到執行機會,這也和sleep()方法不同。

join()

join()方法會使當前線程等待調用join()方法的線程結束後才能繼續執行.

比較

sleep() vs wait()

來自不同的類

這兩個方法來自不同的類,sleep是Thread類的方法,而wait是Object類的方法

//Object.java
public class Object {
    public native int hashCode()
    public boolean equals(Object obj)
    public String toString()
    public final native void notify();
    public final native void notifyAll();
    public final void wait()
}
//Thread
class Thread implements Runnable {
    public static native void yield();
    public static native void sleep(long millis);
    ...
}

釋放鎖

執行sleep方法後不會釋放鎖,而執行wait方法後會釋放鎖.

package com.hit.learn.concurrencyinaction;

public class TestD {

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        try {
            Thread.sleep(1000);
            System.out.println("main thread is work:1s, Thread1 is run and hold lock!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread1 started!");
            synchronized (TestD.class) {
                try {
                    Thread.sleep(5000);
                    //TestD.class.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread1 is over!!!");
            }
        }
    }

    private static class Thread2 implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread2 started!");
            synchronized (TestD.class) {
                System.out.println("Thread2 hold Lock");
                //TestD.class.notify();
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread2 is going on....");
                System.out.println("thread2 is over!!!");
            }
        }
    }
}

執行結果如下:

Thread1 started!
main thread is work:1s, Thread1 is run and hold lock!
Thread2 started!
thread1 is over!!!
Thread2 hold Lock
thread2 is going on....
thread2 is over!!!

Thread1先啓動,sleep了5s.在這期間啓動Thread2 是不能獲得鎖的,會在Thread2中的 synchronized (TestD.class) {} Block住,等待Thread1釋放掉lock之後,才能獲得鎖繼續執行.

package com.hit.learn.concurrencyinaction;

public class TestD {

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        try {
            Thread.sleep(1000);
            System.out.println("main thread is work:1s, Thread1 is run and hold lock!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread1 started!");
            synchronized (TestD.class) {
                try {
                    //Thread.sleep(5000);
                    TestD.class.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread1 is over!!!");
            }
        }
    }

    private static class Thread2 implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread2 started!");
            synchronized (TestD.class) {
                System.out.println("Thread2 hold Lock");
                TestD.class.notify();
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("thread2 is going on....");
                System.out.println("thread2 is over!!!");
            }
        }
    }
}

執行結果如下:

Thread1 started!
main thread is work:1s, Thread1 is run and hold lock!
Thread2 started!
Thread2 hold Lock
thread2 is going on....
thread2 is over!!!
thread1 is over!!!

Thread1先啓動,wait()了5s.在這期間啓動Thread2 是不能獲得鎖的,不會在Thread2中的 synchronized (TestD.class) {} Block住,notify()之後可以繼續執行.

是否需要在同步塊中

wait,notify和notifyAll只能在同步方法或同步代碼塊中調用,而sleep可以在任何地方調用。

    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread1 started!");
            try {
                TestD.class.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

sleep() vs yield()

  • sleep()方法給其他線程運行機會時不考慮其他線程的優先級,因此會給低優先級的線程運行的機會;yield()方法只會給相同優先級或更高優先級的線程運行的機會。
  • 線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態。
  • sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明任何異常。
  • sleep()方法比yield()方法具有更好的可移植性(跟操作系統CPU調度相關)。 sleep方法需要參數,而yield方法不需要參數。

notify() vs notifyAll()

先說兩個概念:鎖池和等待池

  • 鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由於這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。
  • 等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖後,進入到了該對象的等待池中。

然後再來說notify和notifyAll的區別:

  • 如果線程調用了對象的wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
  • 當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它纔會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。

綜上,所謂喚醒線程,另一種解釋可以說是將線程由等待池移動到鎖池,notifyAll調用後,會將全部線程由等待池移到鎖池,然後參與鎖的競爭,競爭成功則繼續執行,如果不成功則留在鎖池等待鎖被釋放後再次參與競爭。而notify只會喚醒一個線程。有了這些理論基礎,後面的notify可能會導致死鎖,而notifyAll則不會的例子也就好解釋了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章