[小笔记] 理解 Java 中的 wait(),notify(),notifyAll() 等方法
相信很多人都有去看过这几个方法的描述,站在面试八股文的角度可能自认为学会了,我感觉很多人并没有真正理解这几个方法,我自己也是经常忘记😂,所以这次单独记录一下,以方便以后自己忘记了再回来查看。
在理解上面的几个方法之前必须理解锁池和等待池的概念:
-
锁池
假如有一个对象ObjectA
,有三个线程T1
、T2
和T3
都通过synchronized
的方式去竞争锁,假如第一次竞争由T2
线程获取了锁,那么T1
和T3
就会被加入对象ObjectA
的锁池,当T2
线程释放锁之后,锁池里面的T1
与T3
线程再重新竞争锁。简单来说就是对象的锁池里面的线程在持有锁的线程释放后他们会自动再去竞争锁。 -
等待池
假如线程T2
通过synchronized
的方式获取了ObjectA
对象的锁后,T2
线程通过ObjectA
对象的wait()
方法(还会单独讲哈)释放对线对象ObjectA
的锁,并且会把T2
线程添加到ObjectA
对象的等待池中。等待池中的线程和锁池中的线程的区别是就算在对象ObjectA
的锁被释放后也不会去主动去竞争锁。只有当等待池中的线程被移动到锁池后才会去重新竞争锁,通过notify()
或者notifyAll()
(还会单独讲)方法可以把等待池中的线程移动到锁池中。
在理解了锁池和等待池的概念后我们再来聊 wait()
,notify()
,notifyAll()
这几个方法,他们都是由 Object
对象实现的。也就是说所有的其他对象也都可以使用这几个方法,这几个方法也都是必须在获取目标对象的 synchronized
锁之后才能够使用。
-
wait()
在获取到对象的synchronized
锁之后,通过调用wait()
方法可以释放当前线程对对象的锁,同时当前线程会被加入到等待池中,当前的线程就会进入WAITING
状态。只有等其他的线程调用notify()
/notifyAll()
方法后,这个线程会被移动到锁池中,在锁池中重新竞争获取到锁后才会把线程状态变为RUNNING
状态,然后继续执行。 -
notify()
在获取到对象的synchronized
锁之后,通过调用notify()
方法可以将等待池中的一个线程移动到锁池中(如果等待池为空,那就没有可移动的线程)。这个方法调用后不会释放对当前对象的锁,只有在当前线程对当前的锁释放后,锁池中的线程才重新竞争。 -
notifyAll()
其他特性都和notify()
方法类似,不过调用notifyAll()
方法后可以将所有的等待池中的线程移动到锁池中。
写了这么多,再来看看几个验证的示例代码把。
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
从等待池中移动到锁池中,所以线程 thread1
在 wait()
后重新获取到锁,最后输出 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)
,后获取锁,这里 thread1
和 thread2
,会先获取到锁后会通过 wait()
方法释放锁,所以上面的输出是:
Thread1 got Lock.
Thread2 got Lock.
也可能是:
Thread2 got Lock.
Thread1 got Lock.
这取决于 thread1
和 thread2
谁先竞争到锁。
由于 thread1
与 thread2
都通过 wait()
释放锁并将其加入等待池,所以 thread3
获取到锁,所以最后的输出是:
Thread3 got Lock.
在 thread3
调用 notify()
后,会将等待池中的一个线程移动到锁池中,我的 demo
中移动的是 thread1
,所以在线程 thread3
释放后 thread1
的 wait()
方法后能够继续获取到锁,所以最后的输出是:
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()
的理解就要深一点了,面试八股文和实战都能够做到游刃有余。
网友评论