美文网首页Java并发编程
Java并发编程 - Java语言规范

Java并发编程 - Java语言规范

作者: HRocky | 来源:发表于2019-03-10 23:15 被阅读0次

此篇文章为《Java语言规范》中"线程和锁"这章节的翻译和说明。

1. Synchronization(同步)

Java语言规范(JSL)在"线程和锁"这章关于同步(Synchronization)有这样的描述:

The Java programming language provides multiple mechanisms for communicating between threads. The most basic of these methods is synchronization, which is implemented using monitors.

Java语言为线程间的通信提供了多种机制,这些方法中最基础的就是同步,同步是通过监视器(Monitors)来实现的。

Each object in Java is associated with a monitor, which a thread can lock or unlock.

Java中每个对象都和一个Monitor关联,

对象和Monitor关联.png

线程可以lock(锁住)和unlock(解锁)监视器,

线程lock&unlock监视器.png

Only one thread at a time may hold a lock on a monitor. Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor.

在任何时刻,都只能有一个线程锁住监视器。任何其他试图锁定该监视器的线程都将阻塞,直至它们可以获得该监视器上的锁。

任何时刻只能有一个线程锁住监视器.png

A thread t may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation.

线程t可以多次锁定某个特定的监视器,而每个解锁操作都会抵消一次锁定操作的效果。

synchronzied语句块说明

The synchronized statement (§14.19) computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed.

synchronized语句获取对象的引用,然后它试图锁定该对象的监视器,在完全锁定成功之前不会执行下一步操作。

After the lock action has been performed, the body of the synchronized statement is executed.

在锁定操作完成后, synchronized语句体就会被执行。

If execution of the body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

如果synchronized语句体执行结束,无论是正常结束还是意外结束,都会自动在相同的监视器上执行解锁动作。

synchronzied方法说明

A synchronized method (§8.4.3.6) automatically performs a lock action when it is invoked; its body is not executed until the lock action has successfully completed.

当synchronzied方法被调用的时候会自动执行锁定操作; 在锁定操作完全完成之前方法体不会被执行。

If the method is an instance method, it locks the monitor associated with the instance for which it was invoked (that is, the object that will be known as this during execution of the body of the method).

如果方法是实例方法,那么就会锁定与调用这个方法的对象(这个对象就是在方法执行期间被称为this的对象)相关联的监视器。

If the method is static, it locks the monitor associated with the Class object that represents the class in which the method is defined.

如果方法是静态方法,那么就锁定与Class对象关联的监视器,这个Class对象就是定义这个方法的类所对应的对象。

If execution of the method's body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

如果该方法体执行结束,无论是正常地结束还是意外地结束,都会在相同的监视器上执行解锁动作。

The Java programming language neither prevents nor requires detection of deadlock conditions. Programs where threads hold (directly or indirectly) locks on multiple objects should use conventional techniques for deadlock avoidance, creating higher-level locking primitives that do not deadlock, if necessary.

Other mechanisms, such as reads and writes of volatile variables and the use of classes in the java.util.concurrent package, provide alternative ways of synchronization.

Java编程语言既不阻止也不要求对死锁情况进行探测。线程(间接会直接)持有多个对象上的锁的程序应该使用避免死锁的惯用技术,如果必需的话,可以创建更高级别的不会死锁的锁定原语。

其他机制,例如对volatile变量的读写和对java.util.concurrent包中类的使用,都提供了同步的可替代方式。

2. Wait Sets and Notification(等待集合和通知)

Every object, in addition to having an associated monitor, has an associated wait set. A wait set is a set of threads.

每个对象,除了具有相关联的监视器,还有相关联的等待集合,这个等待集合是线程集合。

这里要说明一下,Java语言规范中只是规定了对象要跟一个等待集合进行关联,但是没有说怎么样关联,看过JVM相关书籍或者是看过JVM源码中关于ObjectMonitor的同学会知道这个等待集合是Monitor内部的一个属性,下面的图做那样的关联,只是做一个概念性的说明。

关联等待集合.png

When an object is first created, its wait set is empty.

当对象最先被创建时,它的等待集合是空的。

对象刚被创建成功等待集合是空的.png

Elementary actions that add threads to and remove threads from wait sets are atomic.

向等待集合中添加线程或者移除线程的基础动作都是原子性的。

Wait sets are manipulated solely through the methods Object.wait, Object.notify, and Object.notifyAll.

等待集合只能通过Object.wait、Object.notify和Object.notifyAll方法进行操作。

操作等待集合.png

Wait set manipulations can also be affected by the interruption status of a thread, and by the Thread class's methods dealing with interruption. Additionally, the Thread class's methods for sleeping and joining other threads have properties derived from those of wait and notification actions.

等待集合的操作还会受到线程的中断状态和Thread类中处理中断的方法的影响。另外,线程类用于休眠和连接其他线程的方法具有从等待和通知操作派生的属性。

Wait(等待)

Wait actions occur upon invocation of wait(), or the timed forms wait(long millisecs) and wait(long millisecs, int nanosecs).

A call of wait(long millisecs) with a parameter of zero, or a call of wait(long millisecs, int nanosecs) with two zero parameters, is equivalent to an invocation of wait().

等待动作在调用wait(),或者调用具有定时机制的wait(long millisecs)和wait(long millisecs, int nanosecs)时发生。

参数为0的wait(long millisecs)调用或者两个参数都为0的wait(long millisecs, int nanosecs)的调用等价于调用wait()方法。

A thread returns normally from a wait if it returns without throwing an InterruptedException.

如果线程返回时没有抛出InterruptedException,那么该线程就是正常返回的。

设线程t是在对象m上执行wait方法的线程,n是t在m上还没有匹配解锁动作的锁定动作的数量。调用m的wait方法的时候下面的某一种情况会发生:

  • 如果n为(比如说线程t那没有在目标m上进行锁定),那么会抛出 IllegalMonitorStateException异常。
    这种情况说的是在非同步代码块或非同步方法中调用wait方法时会出现的问题。
  • 如果是调用有时间参数的wait方法,并且nanosecs这个参数不在0-999999的范围内或者millisecs是负数,那么就会抛出IllegalArgumentException异常。
    这种情况说的是非法的参数调用会出现的问题。
  • 如果线程t被中断了,那么会抛出InterruptedException异常,并且中断状态会设置成false。

如果上面的情况没有出现,那么就执行下面的序列:

  1. 线程t被添加到对象m的等待集合中,并在m上执行n个解锁操作。
    这里说的添加到等待集合中的线程是会放弃对m的监视器的锁定,wait之前锁定了几次,那么就要解锁几次。
  2. 线程t在被移除m的等待集合之前不会执行任何的指令。该线程可能因下列任何一个动作而被从等待集合中移除,并在之后某个时刻继续执行:
  • 在m上执行notify动作,在该动作中t被选中从等待集合中移除。
  • 在m上执行notifyAll动作。
  • 在t上执行interrupt动作。
  • 如果是定时等待,那么在从该等待动作开始至少millisecs毫秒加nanosecs纳秒的时间流逝之后,一个内置的动作会将t从m的等待集合中移除。

每个线程必须确定可以引发其从等待集合中被移除的事件的顺序。这个顺序不必与其他排序方式一致,但是线程的行为必须是看起来就像这些事件是按照这个顺序发生的一样。

例如,如果线程t在m的等待集合中,并且t的中断和m的通知都发生了,那么这些事件必然有一个顺序。如果是中断被认为是首先发生的,那么t最终以抛出InterruptedException异常而从wait返回,并且在m的等待集合中的某个其他线程(如果在发生通知是存在的话)必须收到这个通知。如果通知被认为是首先发生的,那么t最终会从wait中正常返回,而中断将被悬挂。

  1. 线程t在m上执行n个锁定操作。

  2. 如果线程t在第2步因争端而从m的等待集合被移除,那么t的中断状态会被设置为false,并且wait方法会抛出InterruptedException。

Notification(通知)

通知动作发生在对notify或notifyAll进行调用时。

设线程t是在对象m上执行这两个方法的线程,设n是t在m上还没有匹配解锁动作的锁定动作的数量,那么执行notify或notifyAll的时候,下列的动作之一将会发生:

  • 如果n是0,那么会抛出IllegalMonitorStateException。
    这种情况表示线程t还没有处理目标m的锁。
  • 如果n比0大,并且这是notify动作,那么如果m的等待集合不为空,那么作为m的当前等待集合中的线程u将被选中并从等待集合中移除。
    不能保证等待集合中哪个线程会被选中。从等待几种移除使得u可以在等待动作中继续。但是,需要注意,u在继续执行时的加锁动作只能在t完全解锁m的监视器之后的某个时刻才能成功。
  • 如果n大于0,并且这是notifyAll动作,那么所有线程都会从m的等待集合中移除,因此也就都可以继续执行。

但是,需要注意,其中每次只能有一个线程可以锁定在等待继续的过程中所需的监视器。

Interruptions(中断)

Interruption actions occur upon invocation of Thread.interrupt, as well as methods defined to invoke it in turn, such as ThreadGroup.interrupt.

中断动作发生在对Thread.interrupt进行调用时,以及发生在对定义为依次调用它的方法进行调用时,例如ThreadGroup.interrupt。

Let t be the thread invoking u.interrupt, for some thread u, where t and u may be the same. This action causes u's interruption status to be set to true.

设t是调用u.interrupt的线程,其中u是某个线程,而t和u可以是相同的。这个动作会导致u的中断被设置为true。

Additionally, if there exists some object m whose wait set contains u, then u is removed from m's wait set. This enables u to resume in a wait action, in which case this wait will, after re-locking m's monitor, throw InterruptedException.

另外,如果存在某个对象m,其等待集合包含u,那么u会从m的等待集合中移除。这会使得u可以在等待动作中恢复,在这种情况下,这个等待在重新锁定m的监视器之后,会抛出InterruptedException。

Invocations of Thread.isInterrupted can determine a thread's interruption status. The static method Thread.interrupted may be invoked by a thread to observe and clear its own interruption status.

对Thread.isInterrupted的调用可以确定线程的中断状态。static方法Thread.interrupted可以被线程调用以观察和清除其自身的中断状态。

等待、通知和中断的交互

The above specifications allow us to determine several properties having to do with the interaction of waits, notification, and interruption.

上面的规范使得我们可以确定若干属性都与等待、通知和中断相互作用有关。

如果线程在等待时既被通知了又被中断了,那么可以发生下列中的某种情形:

  • 从wait正常返回,尽管仍然具有悬挂的中断(换句话说,对Thread.interrupted的调用会返回true)。
  • 从wait中以抛出InterruptedException实例而返回。

The thread may not reset its interrupt status and return normally from the call to wait.

线程不可以重置它的中断状态并从对wait的调用中返回。

类似地,通知不能因中断而丢失。假如线程集s在对象m的等待集中,另一个线程m上执行了一个notify,那么下面中的任一种会发生:

  • s中至少有一个线程必须从wait中正常返回,或者
  • s中所有线程都必须抛出InterruptedException而退出wait。

Note that if a thread is both interrupted and woken via notify, and that thread returns from wait by throwing an InterruptedException, then some other thread in the wait set must be notified.

注意,如果一个线程经由notify被中断和唤醒,并且该线程以抛出InterruptedException而从wait返回,那么在等待集中的其他线程必须被通知。

3. 睡眠(sleep)和让步(yield)

Thread.sleep会导致当前运行的线程睡眠(暂时中止执行)指定的时间,具体时间取决于系统定时器和调度器的精度和准确度。睡眠的线程不会丧失对任何监视器的所有权,而恢复执行则依赖于该线程的处理器的调度机制和可用性。

注意到一点很重要:无论是Thread.sleep还是Thread.yield都没有任何同步语义。特使是,编译器不必在调用Thread.sleep或Thread.yield之前将寄存器中缓存的写操作冲刷到共享内存中,也不必调用Thread.sleep或Thread.yield之后重新加载寄存器中缓存的值。

例如,在下面的代码片段中,假设this.do是非volatile的boolean域:

while (!this.done)
    Thread.sleep(1000);

编译器可以只读取this.done域一次,并且在循环的每次迭代中重复用缓存的值。这意味着即使另一个线程修改了this.done的值,该循环永远都不会终止。

相关文章

网友评论

    本文标题:Java并发编程 - Java语言规范

    本文链接:https://www.haomeiwen.com/subject/nqdfpftx.html