[小笔记] 理解 Java 中的 wait(),notify()

2024-01-07  本文已影响0人  BlueSocks

[小笔记] 理解 Java 中的 wait(),notify(),notifyAll() 等方法

相信很多人都有去看过这几个方法的描述,站在面试八股文的角度可能自认为学会了,我感觉很多人并没有真正理解这几个方法,我自己也是经常忘记😂,所以这次单独记录一下,以方便以后自己忘记了再回来查看。

在理解上面的几个方法之前必须理解锁池和等待池的概念:

在理解了锁池和等待池的概念后我们再来聊 wait()notify()notifyAll() 这几个方法,他们都是由 Object 对象实现的。也就是说所有的其他对象也都可以使用这几个方法,这几个方法也都是必须在获取目标对象的 synchronized 锁之后才能够使用。

写了这么多,再来看看几个验证的示例代码把。

wait()notify()

fun main(args: Array<String>) {

    val lock = Any() as Object

    val thread1 = Thread {
       synchronized(lock) {
           println("Thread1 got Lock.")
           Thread.sleep(150)
           lock.wait()
           println("Thread1 after wait")
       }
    }
    thread1.start()

    val thread2 = Thread {
        Thread.sleep(100)
        synchronized(lock) {
            println("Thread2 got Lock.")
            lock.notify()
            Thread.sleep(150)
            println("Thread2 after notify")
        }
    }
    thread2.start()

    thread1.join()
}

最终得到以下输出:

Thread1 got Lock.
Thread2 got Lock.
Thread2 after notify
Thread1 after wait

解释一下为什么是以上的输出:

首先在线程 thread2 开始前 sleep(100) 的目的是为了让线程 thread1 先获取到锁,所以先输出 Thread1 got Lock.

在线程 thread1 获取到锁后 sleep(150) 这时线程 thread2 已经开始竞争锁了,这时线程 thread1 调用 wait() 方法释放锁,同时把自己添加到等待池中,所以这个时候线程 thread2 获取到锁,所以输出 Thread2 got Lock.

在线程 thread2 获取到锁后调用 notify() 方法将线程 thread1 从等待池中移动到锁池中,由于这时 thread2 并没有释放锁虽然做了 sleep(150) 操作,所以还是会先输出 Thread2 after notify

在线程 thread2 释放锁后,由于在之前通过 notify() 方法将 thread1 从等待池中移动到锁池中,所以线程 thread1wait() 后重新获取到锁,最后输出 Thread1 after wait。(如果线程 thread2 中没有调用 notify() 方法,那么 thread1 会陷入无限的等待,永远不会输出 Thread1 after wait,读者可以自己试试)

notify()notifyAll()

fun main(args: Array<String>) {

    val lock = Any() as Object

    val thread1 = Thread {
       synchronized(lock) {
           println("Thread1 got Lock.")
           lock.wait()
           println("Thread1 after wait.")
       }
    }
    thread1.start()

    val thread2 = Thread {
        synchronized(lock) {
            println("Thread2 got Lock.")
            lock.wait()
            println("Thread2 after wait.")
        }
    }
    thread2.start()

    val thread3 = Thread {
        Thread.sleep(100)
        synchronized(lock) {
            println("Thread3 got Lock.")
            lock.notify()
            println("Thread3 after notify.")
        }
    }
    thread3.start()

    thread1.join()
}

最后输出是:

Thread1 got Lock.
Thread2 got Lock.
Thread3 got Lock.
Thread3 after notify.
Thread1 after wait.

解释一下上面你的输出:
这里故意让 thread3 sleep(100),后获取锁,这里 thread1thread2,会先获取到锁后会通过 wait() 方法释放锁,所以上面的输出是:

Thread1 got Lock.
Thread2 got Lock.

也可能是:

Thread2 got Lock.
Thread1 got Lock.

这取决于 thread1thread2 谁先竞争到锁。

由于 thread1thread2 都通过 wait() 释放锁并将其加入等待池,所以 thread3 获取到锁,所以最后的输出是:

Thread3 got Lock.

thread3 调用 notify() 后,会将等待池中的一个线程移动到锁池中,我的 demo 中移动的是 thread1,所以在线程 thread3 释放后 thread1wait() 方法后能够继续获取到锁,所以最后的输出是:

Thread3 after notify.
Thread1 after wait.

由于 thread2 没有被移动到锁池中去,所以他的 wait() 方法将永远等待下去,也就是常说的死锁。

我们为了不让 thread2 永远等下去,我们可以把 thread3 中的方法替换成 notifyAll() 方法:

fun main(args: Array<String>) {

    val lock = Any() as Object

    val thread1 = Thread {
       synchronized(lock) {
           println("Thread1 got Lock.")
           lock.wait()
           println("Thread1 after wait.")
       }
    }
    thread1.start()

    val thread2 = Thread {
        synchronized(lock) {
            println("Thread2 got Lock.")
            lock.wait()
            println("Thread2 after wait.")
        }
    }
    thread2.start()

    val thread3 = Thread {
        Thread.sleep(100)
        synchronized(lock) {
            println("Thread3 got Lock.")
            lock.notifyAll()
            println("Thread3 after notifyAll.")
        }
    }
    thread3.start()

    thread1.join()
}

最后就的输出就是:

Thread1 got Lock.
Thread2 got Lock.
Thread3 got Lock.
Thread3 after notifyAll.
Thread1 after wait.
Thread2 after wait.

最后

到这里相信你就对 wait()notify()notifyAll() 的理解就要深一点了,面试八股文和实战都能够做到游刃有余。

上一篇 下一篇

猜你喜欢

热点阅读