想象一下,你住在一个宿舍,这个宿舍有一个公共浴室,这个浴室相当于一个方法或者一个代码块。由于只有一个浴室,所以每次只能有一个人使用。如果有人正在使用,其他人就必须等待。这个浴室就像Java中的一个monitor,而每个想要使用浴室的人就像一个线程。
在Java中,我们使用synchronized关键字来声明一个方法或者一个代码块是“同步”的,就像我们制定一个规则,每个人在使用浴室之前都必须敲门询问是否可进。如果浴室空闲,那么这个人就可以进去,并且锁住门,这样其他人就不能进去。这个人就像一个线程获得了monitor的锁。
当这个人使用完浴室后,他会解锁门并出来,这样其他在等待的人就可以使用浴室了。这就像一个线程退出synchronized方法或代码块,释放monitor的锁。
如果浴室正在被使用,那么其他人就必须等待,他们不能强行进入。这就像一个线程试图进入一个已经被其他线程锁定的synchronized方法或代码块,它必须等待直到monitor的锁被释放。
这就是synchronized和monitor如何进行线程同步的基本原理。通过这种机制,Java能够保证在任何时刻,只有一个线程能够执行synchronized方法或代码块,从而避免了线程间的冲突和数据不一致的问题。
接下来再引入Monitor的概念应该就不会晦涩了吧:
Monitor中文翻译为管程,也有人称之为“监视器”。它将将共享变量及其对共享变量的操作统一封装起来,由此解决线程安全问题。
Java中的所有对象都可以加锁,只需要这个对象与一个 monitor 相关联。当尝试获得对象的锁的时候,其实就是对该对象的Monitor进行操作。线程可以对 monitor 执行lock 和 unlock 操作。
但Java并没有把lock和unlock操作直接开放给用户使用,但是却提供了两个指令来隐式地使用这两个操作:moniterenter和moniterexit。
moniterenter对应lock操作,moniterexit对应unlock操作,
通过这两个指锁定和解锁 monitor 对象来实现同步。
当一个monitor对象被线程持有后,它将处于锁定状态。对于一个 monitor 而言,同时只能有一个线程能锁定monitor,其它线程试图获得已被锁定的 monitor时,都将被阻塞。当monitor被释放后,阻塞中的线程会尝试获得该 monitor锁。一个线程可以对一个 monitor 反复执行 lock 操作,对应的释放锁时,需要执行相同次数的 unlock 操作。