# 内在锁定和同步

> 原文： [https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html](https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html)

同步是围绕一个称为 _ 内在锁 _ 或 _ 监视器锁 _ 的内部实体构建的。 （API 规范通常将此实体简称为“监视器”。）内部锁在同步的两个方面都发挥作用：强制对对象状态进行独占访问，并建立对可见性至关重要的先发生关系。

每个对象都有一个与之关联的内在锁。按照惯例，需要对对象字段进行独占和一致访问的线程必须 _ 在访问对象之前获取 _ 对象的内部锁定，然后 _ 释放 _ 内部锁定时使用它们。一个线程被认为 _ 拥有 _ 它获得锁定和释放锁定之间的内在锁定。只要一个线程拥有一个内部锁，没有其他线程可以获得相同的锁。另一个线程在尝试获取锁时将阻塞。

当线程释放内部锁时，在该操作与同一锁的任何后续获取之间建立先发生关系。

## 锁定同步方法

当线程调用 synchronized 方法时，它会自动获取该方法对象的内部锁，并在方法返回时释放它。即使返回是由未捕获的异常引起的，也会发生锁定释放。

您可能想知道在调用静态同步方法时会发生什么，因为静态方法与类关联，而不是与对象关联。在这种情况下，线程获取与类关联的`Class`对象的内部锁。因此，对类的静态字段的访问由与该类的任何实例的锁不同的锁控制。

## 同步语句

创建同步代码的另一种方法是使用 _ 同步语句 _。与 synchronized 方法不同，synchronized 语句必须指定提供内部锁的对象：

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

```

在此示例中，`addName`方法需要将更改同步到`lastName`和`nameCount`，但还需要避免同步其他对象方法的调用。 （从同步代码调用其他对象的方法可能会产生 [Liveness](liveness.html) 部分中描述的问题。）如果没有 synchronized 语句，则必须有一个单独的，不同步的方法，仅用于调用`nameList.add` ]。

同步语句对于通过细粒度同步提高并发性也很有用。例如，假设类`MsLunch`有两个实例字段，`c1`和`c2`，它们从不一起使用。必须同步这些字段的所有更新，但没有理由阻止 c1 的更新与 c2 的更新交错 - 这样做会通过创建不必要的阻塞来减少并发性。我们创建两个对象仅用于提供锁，而不是使用同步方法或使用与`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++;
        }
    }
}

```

谨慎使用这个成语。您必须绝对确保对受影响字段的访问进行交错是否安全。

## 可重入同步

回想一下，线程无法获取另一个线程拥有的锁。但是线程 _ 可以 _ 获得它已经拥有的锁。允许线程多次获取相同的锁定会启用 _ 可重入同步 _。这描述了一种情况，其中同步代码直接或间接地调用也包含同步代码的方法，并且两组代码使用相同的锁。在没有可重入同步的情况下，同步代码必须采取许多额外的预防措施，以避免线程导致自身阻塞。