共计 898 个字符,预计需要花费 3 分钟才能阅读完成。
上图标注的位置是添加 Synchronized 关键字之后独有的。执行同步代码块后首先要先执行 monitorenter 指令,退出的时候 monitorexit 指令。通过分析可以看出,使用 Synchronized 进行同步的关键是必须要获取对象的监视器 monitor,当线程获取 monitor 后才能继续往下执行,否则就只能等待。而这个获取的过程是 互斥 的,同一时刻只有一个线程能够获取到 monitor。在同一锁程中,线程不需要再次获取同一把锁。Synchronized 先天具有重入性。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一。任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法,如果没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入到 BLOCKED 状态。
wait() notify() notifyAll() 三个方法都是 Object 的方法,并不是线程的方法
wait()
释放占有的对象锁,线程进入等待池,释放 cpu, 而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。
sleep()
线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放 cpu,但并不释放对象锁。在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得 cpu, 执行代码。wait()和 sleep()最大的不同在于 wait()会释放对象锁,而 sleep()不会。
notify()
该方法会唤醒因为调用对象的 wait()而等待的线程,其实就是 对对象锁的唤醒,从而使得 wait()的线程可以有机会获取对象锁 。调用 notify() 后,并不会立即释放锁,而是继续执行当前代码,直到 synchronized 中的代码全部执行完毕,才会释放对象锁。JVM 则会在等待的线程中调度一个线程去获得对象锁并执行代码。wait()和 notify()必须在 synchronized 代码块中调用。
notifyAll() 唤醒所有等待的线程。