Documentation

The Java™ Tutorials
Hide TOC
Intrinsic Locks and Synchronization内在锁和同步
Trail: Essential Java Classes
Lesson: Concurrency
Section: Synchronization

Intrinsic Locks and Synchronization内在锁和同步

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. 同步是围绕称为内在锁监视锁的内部实体构建的。(The API specification often refers to this entity simply as a "monitor.") (API规范通常将该实体简单地称为“监视器”)Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an object's state and establishing happens-before relationships that are essential to visibility.内在锁在同步的两个方面都起作用:强制对对象状态的独占访问,以及在对可见性至关重要的关系之前建立。

Every object has an intrinsic lock associated with it. 每个对象都有一个与之关联的内在锁。By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. 按照惯例,需要以独占和一致方式访问对象字段的线程必须在访问对象字段之前获取对象的固有锁,然后在处理完这些字段后释放固有锁。A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. 线程在获取锁和释放锁之间拥有内在锁。As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. 只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。The other thread will block when it attempts to acquire the lock.另一个线程在尝试获取锁时将阻塞。

When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.当一个线程释放一个内在锁时,在该操作和该锁的任何后续获取之间建立关系之前,会发生一个错误。

Locks In Synchronized Methods同步方法中的锁

When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. 当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在该方法返回时释放它。The lock release occurs even if the return was caused by an uncaught exception.

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. 您可能想知道调用静态同步方法时会发生什么,因为静态方法与类而不是对象相关联。In this case, the thread acquires the intrinsic lock for the Class object associated with the class. 在这种情况下,线程获取与类关联的Class对象的内在锁。Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.因此,对类的静态字段的访问由一个锁控制,该锁不同于类的任何实例的锁。

Synchronized Statements同步语句

Another way to create synchronized code is with synchronized statements. 创建同步代码的另一种方法是使用同步语句Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:与同步方法不同,同步语句必须指定提供内部锁的对象:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other objects' methods. 在本例中,addName方法需要同步对lastNamenameCount的更改,但还需要避免同步调用其他对象的方法。(Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.) (从同步代码调用其他对象的方法可能会产生问题,这些问题将在活性一节中介绍。)Without synchronized statements, there would have to be a separate, unsynchronized method for the sole purpose of invoking nameList.add.如果没有同步语句,就必须有一个单独的、未同步的方法来调用nameList.add

Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Synchronized语句对于通过细粒度同步提高并发性也很有用。Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. 例如,假设类MsLunch有两个实例字段c1c2,它们从未一起使用过。All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. 这些字段的所有更新都必须同步,但没有理由阻止c1的更新与c2的更新交错—这样做通过创建不必要的阻塞来减少并发性。Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.我们创建两个对象来单独提供锁,而不是使用同步方法或使用与this相关联的锁。

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Use this idiom with extreme care. 要非常小心地使用这个成语。You must be absolutely sure that it really is safe to interleave access of the affected fields.您必须绝对确保交错访问受影响的字段确实是安全的。

Reentrant Synchronization重入同步

Recall that a thread cannot acquire a lock owned by another thread. 回想一下,一个线程无法获取另一个线程拥有的锁。But a thread can acquire a lock that it already owns. 但是线程可以获得它已经拥有的锁。Allowing a thread to acquire the same lock more than once enables reentrant synchronization. 允许线程多次获取相同的锁可以实现可重入同步。This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. 这描述了一种情况,同步代码直接或间接地调用一个也包含同步代码的方法,并且两组代码使用相同的锁。Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.如果没有可重入同步,同步代码必须采取许多额外的预防措施,以避免线程导致自身阻塞。


Previous page: Synchronized Methods
Next page: Atomic Access