📄 17.doc.html
字号:
<html>
<head>
<title>The Java Language Specification Threads and Locks</title>
</head>
<body BGCOLOR=#eeeeff text=#000000 LINK=#0000ff VLINK=#000077 ALINK=#ff0000>
<a href="index.html">Contents</a> | <a href="16.doc.html">Prev</a> | <a href="18.doc.html">Next</a> | <a href="j.index.doc1.html">Index</a>
<hr><br>
<a name="26250"></a>
<p><strong>
CHAPTER 17 </strong></p>
<a name="30206"></a>
<h1>Threads and Locks</h1>
<hr><p>
<a name="44127"></a>
While most of the discussion in the preceding chapters is concerned only with
the behavior of Java code as executed a single statement or expression at a time,
that is, by a single <i>thread</i>, each Java Virtual Machine can support many threads of
execution at once. These threads independently execute Java code that operates on
Java values and objects residing in a shared main memory. Threads may be supported
by having many hardware processors, by time-slicing a single hardware
processor, or by time-slicing many hardware processors.
<p><a name="28270"></a>
Java supports the coding of programs that, though concurrent, still exhibit deterministic behavior, by providing mechanisms for <i>synchronizing</i> the concurrent activity of threads. To synchronize threads, Java uses <i>monitors</i>, which are a high-level mechanism for allowing only one thread at a time to execute a region of code protected by the monitor. The behavior of monitors is explained in terms of <i>locks</i>; there is a lock associated with each object.<p>
<a name="29608"></a>
The <code>synchronized</code> statement <a href="14.doc.html#79287">(§14.17)</a> performs two special actions relevant only to multithreaded operation: (1) after computing a reference to an object but before executing its body, it <i>locks</i> a lock associated with the object, and (2) after execution of the body has completed, either normally or abruptly, it <i>unlocks</i> that same lock. As a convenience, a method may be declared <code>synchronized</code>; such a method behaves as if its body were contained in a <code>synchronized</code> statement.<p>
<a name="29615"></a>
The methods <code>wait</code> (<a href="javalang.doc1.html#33394">§20.1.6</a>, <a href="javalang.doc1.html#14926">§20.1.7</a>, <a href="javalang.doc1.html#32520">§20.1.8</a>), <code>notify</code> <a href="javalang.doc1.html#13789">(§20.1.9)</a>, and <code>notifyAll</code>  <a href="javalang.doc1.html#13790">(§20.1.10)</a> of class <code>Object</code> support an efficient transfer of control from one thread to another. Rather than simply "spinning" (repeatedly locking and unlocking an object to see whether some internal state has changed), which consumes  computational effort, a thread can suspend itself using <code>wait</code> until such time as another thread awakens it using <code>notify</code>. This is especially appropriate in situations where threads have a producer-consumer relationship (actively cooperating on a common goal) rather than a mutual exclusion relationship (trying to avoid conflicts while sharing a common resource).<p>
<a name="28284"></a>
As a thread executes code, it carries out a sequence of actions. A thread may <i>use</i> the value of a variable or <i>assign</i> it a new value. (Other actions include arithmetic operations, conditional tests, and method invocations, but these do not involves variables directly.) If two or more concurrent threads act on a shared variable, there is a possibility that the actions on the variable will produce timing-dependent results. This dependence on timing is inherent in concurrent programming, producing one of the few places in Java where the result of executing a program is not determined solely by this specification.<p>
<a name="28285"></a>
Each thread has a working memory, in which it may keep copies of the values of variables from the main memory that is shared between all threads. To access a shared variable, a thread usually first obtains a lock and flushes its working memory. This guarantees that shared values will be thereafter be loaded from the shared main memory to the threads working memory. When a thread unlocks a lock it guarantees the values it holds in its working memory will be written back to the main memory.<p>
<a name="28524"></a>
This chapter explains the interaction of threads with the main memory, and thus with each other, in terms of certain low-level actions. There are rules about the order in which these actions may occur. These rules impose constraints on any implementation of Java, and a Java programmer may rely on the rules to predict the possible behaviors of a concurrent Java program. The rules do, however, intentionally give the implementor certain freedoms; the intent is to permit certain standard hardware and software techniques that can greatly improve the speed and efficiency of concurrent code.<p>
<a name="28527"></a>
Briefly put, these are the important consequences of the rules:<p>
<ul><a name="28549"></a>
<li>Proper use of synchronization constructs will allow reliable transmission of values or sets of values from one thread to another through shared variables.
<a name="28553"></a>
<li>When a thread uses the value of a variable, the value it obtains is in fact a value stored into the variable by that thread or by some other thread. This is true even if the program does not contain code for proper synchronization. For example, if two threads store references to different objects into the same reference value, the variable will subsequently contain a reference to one object or the other, not a reference to some other object or a corrupted reference value. (There is a special exception for <code>long</code> and <code>double</code> values; see <a href="17.doc.html#28733">§17.4</a>.)
<a name="28556"></a>
<li>In the absence of explicit synchronization, a Java implementation is free to update the main memory in an order that may be surprising. Therefore the programmer who prefers to avoid surprises should use explicit synchronization.
</ul><a name="28287"></a>
<h2>17.1 Terminology and Framework</h2>
<a name="28288"></a>
A <i>variable</i> is any location within a Java program that may be stored into. This
includes not only class variables and instance variables but also components of
arrays. Variables are kept in a <i>main memory</i> that is shared by all threads. Because
it is impossible for one thread to access parameters or local variables of another
thread, it doesn't matter whether parameters and local variables are thought of as
residing in the shared main memory or in the working memory of the thread that
owns them.
<p><a name="45672"></a>
Every thread has a <i>working memory</i> in which it keeps its own <i>working copy</i> of variables that it must use or assign. As the thread executes a Java program, it operates on these working copies. The main memory contains the <i>master copy</i> of every variable. There are rules about when a thread is permitted or required to transfer the contents of its working copy of a variable into the master copy or vice versa<p>
<a name="28291"></a>
The main memory also contains <i>locks</i>; there is one lock associated with each object. Threads may compete to acquire a lock.<p>
<a name="28292"></a>
For the purposes of this chapter, the verbs <i>use</i>, <i>assign</i>, <i>load</i>, <i>store</i>, <i>lock</i>, and <i>unlock</i> name <i>actions</i> that a thread can perform. The verbs <i>read</i>, <i>write</i>, <i>lock</i>, and <i>unlock</i> name actions that the main memory subsystem can perform. Each of these actions is <i>atomic</i> (indivisible). <p>
<a name="28293"></a>
A <i>use</i> or <i>assign</i> action is a tightly coupled interaction between a thread's execution engine and the thread's working memory. A <i>lock</i> or <i>unlock</i> action is a tightly coupled interaction between a thread's execution engine and the main memory. But the transfer of data between the main memory and a thread's working memory is loosely coupled. When data is copied from the main memory to a working memory, two actions must occur: a <i>read</i> action performed by the main memory followed some time later by a corresponding <i>load</i> action performed by the working memory. When data is copied from a working memory to the main memory, two actions must occur: a <i>store</i> action performed by the working memory followed some time later by a corresponding <i>write</i> action performed by the main memory. There may be some transit time between main memory and a working memory, and the transit time may be different for each transaction; thus actions initiated by a thread on different variables may viewed by another thread as occurring in a different order. For each variable, however, the actions in main memory on behalf of any one thread are performed in the same order as the corresponding actions by that thread. (This is explained in greater detail below.)<p>
<a name="28294"></a>
A single Java thread issues a stream of <i>use</i>, <i>assign</i>, <i>lock</i>, and <i>unlock</i> actions as dictated by the semantics of the Java program it is executing. The underlying Java implementation is then required additionally to perform appropriate <i>load</i>, <i>store</i>, <i>read</i>, and <i>write</i> actions so as to obey a certain set of constraints, explained below. If the Java implementation correctly follows these rules and the Java application programmer follows certain other rules of programming, then data can be reliably transferred between threads through shared variables. The rules are designed to be "tight" enough to make this possible but "loose" enough to allow hardware and software designers considerable freedom to improve speed and throughput through such mechanisms as registers, queues, and caches.<p>
<a name="28295"></a>
Here are the detailed definitions of each of the actions:<p>
<ul><a name="28296"></a>
<li>A <i>use</i> action (by a thread) transfers the contents of the thread's working copy of a variable to the thread's execution engine. This action is performed whenever a thread executes a virtual machine instruction that uses the value of a variable.
<a name="28297"></a>
<li>An <i>assign</i> action (by a thread) transfers a value from the thread's execution engine into the thread's working copy of a variable. This action is performed whenever a thread executes a virtual machine instruction that assigns to a variable.
<a name="28298"></a>
<li>A <i>read</i> action (by the main memory) transmits the contents of the master copy of a variable to a thread's working memory for use by a later <i>load</i> action.
<a name="28299"></a>
<li>A <i>load </i>action (by a thread) puts a value transmitted from main memory by a <i>read</i> action into the thread's working copy of a variable.
<a name="28300"></a>
<li>A <i>store </i>action (by a thread) transmits the contents of the thread's working copy of a variable to main memory for use by a later <i>write</i> action.
<a name="28301"></a>
<li>A <i>write</i> action (by the main memory) puts a value transmitted from the thread's working memory by a <i>store</i> action into the master copy of a variable in main memory.
<a name="28303"></a>
<li>A <i>lock</i> action (by a thread tightly synchronized with main memory) causes a thread to acquire one claim on a particular lock.
<a name="28304"></a>
<li>An <i>unlock</i> action (by a thread tightly synchronized with main memory) causes a thread to release one claim on a particular lock.
</ul><a name="28305"></a>
Thus the interaction of a thread with a variable over time consists of a sequence of <i>use</i>, <i>assign</i>, <i>load</i>, and <i>store</i> actions. Main memory performs a <i>read</i> action for every <i>load</i> and a <i>write</i> action for every <i>store</i>. A thread's interactions with a lock over time consists of a sequence of <i>lock</i> and <i>unlock</i> actions. All the globally visible behavior of a thread thus comprises all the thread's actions on variables and locks.<p>
<a name="28920"></a>
<h2>17.2 Execution Order</h2>
<a name="28957"></a>
The rules of execution order constrain the order in which certain events may
occur. There are four general constraints on the relationships among actions:
<p><ul><a name="28958"></a>
<li>The actions performed by any one thread are totally ordered; that is, for any two actions performed by a thread, one action precedes the other.
<a name="28976"></a>
<li>The actions performed by the main memory for any one variable are totally ordered; that is, for any two actions performed by the main memory on the same variable, one action precedes the other.
<a name="28980"></a>
<li>The actions performed by the main memory for any one lock are totally ordered; that is, for any two actions performed by the main memory on the same lock, one action precedes the other.
<a name="28959"></a>
<li>It is not permitted for an action to follow itself.
</ul><a name="28990"></a>
The last rule may seem trivial, but it does need to be stated separately and explicitly
for completeness. Without it, it would be possible to propose a set of actions
by two or more threads and precedence relationships among the actions that
would satisfy all the other rules but would require an action to follow itself.
<p><a name="28951"></a>
Threads do not interact directly; they communicate only through the shared main memory. The relationships between the actions of a thread and the actions of main memory are constrained in three ways: <p>
<ul><a name="28923"></a>
<li>Each <i>lock</i> or <i>unlock</i> action is performed jointly by some thread and the main memory.
<a name="28924"></a>
<li>Each <i>load</i> action by a thread is uniquely paired with a <i>read</i> action by the main memory such that the <i>load</i> action follows the <i>read</i> action.
<a name="28925"></a>
<li>Each <i>store</i> action by a thread is uniquely paired with a <i>write</i> action by the main memory such that the <i>write</i> action follows the <i>store</i> action.
</ul><a name="45036"></a>
Most of the rules in the following sections further constrain the order in which certain actions take place. A rule may state that one action must precede or follow some other action. Note that this relationship is transitive: if action <i>A</i> must precede action <i>B</i>, and <i>B</i> must precede <i>C</i>, then <i>A</i> must precede <i>C</i>. The programmer must remember that these rules are the <i>only</i> constraints on the ordering of actions; if no rule or combination of rules implies that action <i>A</i> must precede action <i>B</i>, then a Java implementation is free to perform action <i>B</i> before action <i>A</i>, or to perform action <i>B</i> concurrently with action <i>A</i>. This freedom can be the key to good performance. Conversely, an implementation is not required to take advantage of all the freedoms given it.<p>
<a name="28927"></a>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -