Java 提供了一种内置的锁机制来支持原子性:同步代码块。
同步代码块包含两个部分:一个作为锁的对象引用,一个作为由这个锁保护的代码块。
以关键字 synchronized
修饰的方法就是横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象。静态的 synchronized
方法以 Class 对象作为锁。
每个 Java 对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁或监视器。
线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁,而无论是通过正常的控制路径退出,还是通过从代码块中抛出异常退出。获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。
Java 的内置锁相当于一种互斥体,也就是说最多只有一个线程能持有这种锁。
如果某个线程试图获得一个已经由它持有的锁,那么这个线程将会成功。
重入的一种实现方法是,为每个锁关联一个计数器和一个所有者线程。当计数值为 0 时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM 将记下锁的持有者,并且将计数值置为 1。如果同一个线程再次获取这个锁,计数值将递增,而当线程退出同步代码块时,计数值相应的递减。当计数值为 0 时,这个锁将被释放。
一种常见的加锁约定是:将所有可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径进行同步,使得在该对象上不会发生并发访问。