The Java™ Tutorials — Concurrency :Intrinsic Locks and Synchronization 内置锁和同步

来源:互联网 发布:数据挖掘的环节包括 编辑:程序博客网 时间:2024/05/22 04:24

The Java™ Tutorials — Concurrency :Intrinsic Locks and Synchronization 内置锁和同步

原文地址:https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

关键点

  • 同步的实现原理:同步是通过一种名为内置锁或者监控锁的内置对象实现的
  • 利用synchronized关键字为对象上锁,提高并发性
  • 可重入同步:允许一个同步代码块调用另一个使用同一把锁的同步代码块
  • 一个线程调用了某同步方法时,它自动会获取此方法对象的内置锁,并在方法返回后自动释放此锁。

全文翻译

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.”) 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.

同步是通过一种名为内置锁或者监控锁的内置对象实现的(API文档常常将此简单的称为“monitor(监控)”。内置锁在同步中扮演了两个方面的角色:增强对对象状态的专一性访问,以及建立对可见性至关重要的先行关系。

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. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.

你也许困惑当调用了一个静态同步方法时究竟会发生什么。由于静态方法是类所有的,而非对象所有,在此情况下,线程就会获取到与此类相关的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. (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.

在本例中,这个addName方法需要同步地改变lastName和nameCount,但是也需要避免对此对象其他方法的同步调用(从同步代码块中调用其他对象的方法可能引起《活跃度》一章中所讨论的问题)。如果没有同步语句,上面的两个目的就需要分步进行,为了调用nameList.add这一个目的我们就需要建立非同步方法。

Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. 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. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.

同步语句同时也对提高更细粒度的并发很有效。举个例子,假如MsLunch类有c1和c2的两个变量,并且它们从未被一起使用过。所有对这些变量的更新操作必须是同步的,但是没有理由阻止c1和c2的交错更新。如果加以阻止,那么就会制造出不必要的阻塞,进而导致并发性的降低。在不使用同步方法,以及于此相关的锁的情况下,我们创建了两个对象来分别提供锁。

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.

在我们的印象中,一个线程无法获取另一个线程拥有的锁。但是一个线程却可以获取一把它已经有的锁。允许一个线程多次获取同一把锁的机制实现了可重入同步。可重入同步描述了这样一个场景,同步代码块可以直接或间接地调用包含另一个同步代码块的方法,而且这两个同步代码块都使用了同一把锁。如果没有可重入同步,同步代码块不得不采取额外的措施防止一个线程因自我调用而阻塞。

0 0
原创粉丝点击