synchronized塊中的wait()、nofity()、nofityAll()方法


前言

  在Java並發編程實戰,會經常遇到多個線程訪問同一個資源的情況,這個時候就需要維護數據的一致性,否則會出現各種數據錯誤,其中一種同步方式就是利用Synchronized關鍵字執行鎖機制,鎖機制是先給共享資源上鎖,只有拿到鎖的線程才可以訪問共享資源,其他線程進入等待狀態。下面將以實例代碼講解一下

一、wait()、nofity()、nofityAll()講解

  示例代碼

package thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by StoneGeek on 2018/5/19.
 * 博客地址:http://www.cnblogs.com/sxkgeek
 * 當線程執行wait()時,會把當前的鎖釋放,然后讓出CPU,進入等待狀態。
 * 當線程執行notify()/notifyAll()方法時,會喚醒一個處於等待狀態該對象鎖的線程,然后繼續往下執行,直到執行完退出對象鎖鎖住的區域(synchronized修飾的代碼塊)后再釋放鎖
 * 個人認為synachronized(){}執行完后會釋放鎖
 */
public class WaitNotify {
    static boolean flag = true;
    static Object lock = new Object();

    public static void main(String[] args) throws Exception {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {
        public void run() {
            // 加鎖,擁有lock的Monitor
            synchronized (lock) {
                // 當條件不滿足時,繼續wait,同時釋放了lock的鎖
                while (flag) {
                    System.out.println(Thread.currentThread().getName()
                            + " flag is true. wait@ "
                            + new SimpleDateFormat("HH:mm:ss")
                            .format(new Date()));
                    try {
                        lock.wait();
                        System.out.println("此處繼續執行"+Thread.currentThread().getName());
//                        flag=true;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 條件滿足時,完成工作
                System.out.println(Thread.currentThread().getName()
                        + " flag is false. running@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
            synchronized (lock){
                System.out.println(Thread.currentThread().getName()+"執行結束");
            }
        }
    }

    // wait()會立刻釋放synchronized(obj)中的obj鎖,以便其他線程可以執行obj.notify()
    // 但是notify()不會立刻立刻釋放sycronized(obj)中的obj鎖,必須要等notify()所在線程執行完synchronized(obj)塊中的所有代碼才會釋放這把鎖.
    // yield(),sleep()不會釋放鎖
    static class Notify implements Runnable {
        public void run() {
            // 加鎖,擁有lock的Monitor
            synchronized (lock) {
                // 獲取lock的鎖,然后進行通知,通知時不會釋放lock的鎖,
                // 直到當前線程釋放了lock后,WaitThread才能從wait方法中返回
                System.out.println(Thread.currentThread().getName()
                        + " hold lock. notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
            }
            // 再次加鎖
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()
                        + " hold lock again. sleep@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
            synchronized (lock){
                System.out.println(Thread.currentThread().getName()+"執行結束");
            }
        }
    }
}

  執行結果如下

1  WaitThread flag is true. wait@ 20:50:39
2  NotifyThread hold lock. notify @ 20:50:40
3  NotifyThread hold lock again. sleep@ 20:50:40
4  NotifyThread執行結束
5  此處繼續執行WaitThread
6  WaitThread flag is false. running@ 20:50:40
7  WaitThread執行結束

  解釋:

  首先創建一個lock對象,然后給這個lock上鎖來對多個進程同步,flag是一個標志,用來跳出while循環。

     當線程執行wait()時,會把當前的鎖釋放,然后讓出CPU,進入等待狀態。

  此時輪到notifythread線程,並且執行notifyAll(),這個意思是能夠喚醒所有正在等待這個lock對象的monitor的線程,但是

  必須要等notify()所在線程執行完synchronized(obj)塊中的所有代碼才會釋放這把鎖,

  此時接着waitthread被喚醒,繼續執行while循環,執行完之后,由於flag在notifythread中置為false,所以跳出while循環(如果在實例代碼的wail()后加flag=true結果是截然不同,由於notirythread進程執行完,此時會一直陷入wait,大家可以試試),

  執行console打印5 6 7

  notify()與notifyAll()的區別 

  notify()方法能夠喚醒一個正在等待該對象的monitor的線程,當有多個線程都在等待該對象

  的monitor的話,則只能喚醒其中一個線程

  而調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程

  當時的疑惑

  (1)既然notify或者notifyAll需要執行完synchronized塊中的內容,那么他還有什么存在的價值的

    后來執行完之后,才發現要是沒有這個方法,那么synchronized塊執行完之后,waitthread還是在等待狀態,無法被喚醒。

  (2)wait被notify喚醒之后,是接着執行,所以console打印5,並不是從頭執行(如果在實例代碼的wail()后加flag=true結果是截然不同,由於notirythread進程執行完,waitthread進程重新執行wait方法,此時會一直陷入wait,無其他進程喚醒此進程)

二、wait()/wait(long)和sleep(long)方法的區別

  將示例代碼中的lock.wait()改為Thread.sleep(1000),console打印

WaitThread flag is true. wait@ 21:29:49
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:50
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:51
此處繼續執行WaitThread
WaitThread flag is true. wait@ 21:29:52
此處繼續執行WaitThread

  由此說明sleep並沒有釋放鎖。

  區別:

    1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法

    2、Sleep(long)可以放在sychnoized塊內也可以不放在里面,但是wait()/wait(long)必須放在語句塊內

    3、Sleep(long)不釋放鎖,只是讓當前線程暫停一段時間,而wait()/wait(long)是釋放鎖

    4、wait()將當前線程放到阻塞隊列,只有調用notify()/notifyAll()方法后,才將其從阻塞隊列中移動到就緒隊列,等待被CPU調度,而wait(long)方法執行后就是放到阻塞隊列,等待時間到期或者被wait()/wait(long)喚醒后就可以放到就緒隊列被CPU調度

  目前還有一個疑惑,就是線程中的run方法有兩個同樣的synchroized(lock),是不是跟一個synchroized(lock)效果是一樣的,目前就運行結果來看是這樣子的!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM