wait是指在一個已經進入了同步鎖的線程內,讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖並運行,只有其他線程調用了notify方法(notify並不釋放鎖,只是告訴調用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因為鎖還在別人手里,別人還沒釋放),調用wait方法的一個或多個線程就會解除wait狀態,重新參與競爭對象鎖,程序如果可以再次得到鎖,就可以繼續向下運行。
1)wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。
2)當前線程必須擁有此對象的monitor(即鎖),才能調用某個對象的wait()方法能讓當前線程阻塞,
(這種阻塞是通過提前釋放synchronized鎖,重新去請求鎖導致的阻塞,這種請求必須有其他線程通過notify()或者notifyAll()喚醒重新競爭獲得鎖)
3)調用某個對象的notify()方法能夠喚醒一個正在等待這個對象的monitor的線程,如果有多個線程都在等待這個對象的monitor,則只能喚醒其中一個線程;
(notify()或者notifyAll()方法並不是真正釋放鎖,必須等到synchronized方法或者語法塊執行完才真正釋放鎖)
4)調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程,喚醒的線程獲得鎖的概率是隨機的,取決於cpu調度
例子1(錯誤使用導致線程阻塞):三個線程,線程3先擁有sum對象的鎖,然后通過sum.notify()方法通知等待sum鎖的線程去獲得鎖,但是這個時候線程1,2並沒有處於wait()導致的阻塞狀態,而是在synchronized方法塊處阻塞了,所以,這次notify()根本沒有通知到線程1,2。然后線程3正常結束,釋放掉sum鎖,這個時候,線程1就立刻獲得了sum對象的鎖(通過synchronized獲得),然后調用sum.wait()方法釋放掉sum的鎖,線程2隨后獲得了sum對象的線程鎖(通過synchronized獲得),這個時候線程1,2都處於阻塞狀態,但是悲催的是,這之后再也沒有線程主動調用sum.notify()或者notifyAll()方法顯示喚醒這兩個線程,所以程序阻塞
- public class CyclicBarrierTest {
- public static void main(String[] args) throws Exception {
- final Sum sum=new Sum();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread3 get lock");
- sum.sum();
- sum.notifyAll(); //此時喚醒沒有作用,沒有線程等待
- Thread.sleep(2000);
- System.out.println("thread3 really release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread1 get lock");
- sum.wait();//主動釋放掉sum對象鎖
- System.out.println(sum.total);
- System.out.println("thread1 release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread2 get lock");
- sum.wait(); //釋放sum的對象鎖,等待其他對象喚醒(其他對象釋放sum鎖)
- System.out.println(sum.total);
- System.out.println("thread2 release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- }
- }
- class Sum{
- public Integer total=0;
- public void sum() throws Exception{
- total=100;
- Thread.sleep(5000);
- }
- }
運行結果:
- thread3 get lock
- thread3 really release lock
- thread2 get lock
- thread1 get lock
- //程序后面一直阻塞
例子2:還是上面程序,順序不同,把線程3放到最下面。最后線程1,2都因為沒有再次獲得線程導致線程阻塞
運行過程:
線程1先運行獲得sum對象鎖(通過synchronized),但是隨后執行了sum.wait()方法,主動釋放掉了sum對象鎖,然后線程2獲得了sum對象鎖(通過synchronized),也通過sum.wait()失去sum的對象鎖,最后線程3獲得了sum對象鎖(通過synchronized),主動通過sum.notify()通知了線程1或者2,假設是1,線程1重新通過notify()/notifyAll()的方式獲得了鎖,然后執行完畢,隨后線程釋放鎖,然后這個時候線程2成功獲得鎖,執行完畢。
- public class CyclicBarrierTest {
- public static void main(String[] args) throws Exception {
- final Sum sum=new Sum();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread1 get lock");
- sum.wait();//主動釋放sum對象鎖,等待喚醒
- System.out.println(sum.total);
- System.out.println("thread1 release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread2 get lock");
- sum.wait(); //主動釋放sum對象鎖,等待喚醒
- System.out.println(sum.total);
- System.out.println("thread2 release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- synchronized (sum) {
- System.out.println("thread3 get lock");
- sum.sum();
- sum.notifyAll();//喚醒其他等待線程(線程1,2)
- Thread.sleep(2000);
- System.out.println("thread3 really release lock");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- }
- }
- class Sum{
- public Integer total=0;
- public void sum() throws Exception{
- total=100;
- Thread.sleep(5000);
- }
- }
執行結果:
- thread1 get lock
- thread2 get lock
- thread3 get lock
- thread3 really release lock
- 100
- thread2 release lock
- 100
- thread1 release lock
轉自 https://blog.csdn.net/azhegps/article/details/63031562