📄 threads.doc.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><html><head><title>VM Spec Threads and Locks</title></head><body BGCOLOR=#eeeeff text=#000000 LINK=#0000ff VLINK=#000077 ALINK=#ff0000><table width=100%><tr><td><a href="VMSpecTOC.doc.html">Contents</a> | <a href="Compiling.doc.html">Prev</a> | <a href="Mnemonics.doc.html">Next</a> | <a href="VMSpecIX.fm.html">Index</a></td><td align=right><i><i>The Java<sup><font size=-2>TM</font></sup> Virtual Machine Specification</i></i></td></tr></table><hr><br> <a name="21293"></a><p><strong>CHAPTER 8 </strong></p><a name="21294"></a><h1>Threads and Locks</h1><hr><p>This chapter details the low-level actions that may be used to explain the interactionof Java virtual machine threads with a shared main memory. It has been adapted with minimal changes from Chapter 17 of the first edition of <i>The Java</i><sup><font size=-2>TM</font></sup><i> Language Specification</i>, by James Gosling, Bill Joy, and Guy Steele.<p><a name="22197"></a><hr><h2>8.1 Terminology and Framework</h2>A <i>variable</i> is any location within a program that may be stored into. This includes not only class variables and instance variables, but also components of arrays. Variablesare kept in a <i>main memory</i> that is shared by all threads. Because it is impossiblefor one thread to access parameters or local variables of another thread, it does not 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="24912"></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 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="22200"></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="22201"></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 actions 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 operations is atomic (indivisible). <p><a name="22202"></a>A <i>use</i> or <i>assign</i> operation 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> operation 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> operation performed by the main memory, followed some time later by a corresponding <i>load</i> operation 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> operation performed by the working memory, followed some time later by a corresponding <i>write</i> operation 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, operations initiated by a thread on different variables may be viewed by another thread as occurring in a different order. For each variable, however, the operations in main memory on behalf of any one thread are performed in the same order as the corresponding operations by that thread. (This is explained in greater detail later.)<p><a name="22203"></a>A single thread issues a stream of <i>use</i>, <i>assign</i>, <i>lock</i>, and <i>unlock</i> operations as dictated by the semantics of the program it is executing. The underlying Java virtual machine implementation is then required additionally to perform appropriate <i>load</i>, <i>store</i>, <i>read</i>, and <i>write</i> operations so as to obey a certain set of constraints, explained later. If the implementation correctly follows these rules and the 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="22204"></a>Here are the detailed definitions of each of the operations:<p><ul><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.<p><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.<p><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> operation.<p><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.<p><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> operation.<p><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.<p><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.<p><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="22213"></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> operations. Main memory performs a <i>read</i> operation for every <i>load</i> and a <i>write</i> operation for every <i>store</i>. A thread's interactions with a lock over time consist of a sequence of <i>lock</i> and <i>unlock</i> operations. All the globally visible behavior of a thread thus comprises all the thread's operations on variables and locks.<p><a name="22214"></a><hr><h2>8.2 Execution Order and Consistency</h2>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><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.<p><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.<p><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.<p><li>It is not permitted for an action to follow itself.</ul>The last rule may seem trivial, but it does need to be stated separately and explicitly for completeness. Without the rule, 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="22221"></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><li>Each <i>lock</i> or <i>unlock</i> action is performed jointly by some thread and the main memory.<p><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.<p><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="22225"></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 virtual machine 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="22226"></a>In the rules that follow, the phrasing "<i>B</i> must intervene between <i>A</i> and <i>C</i>" means that action <i>B</i> must follow action <i>A</i> and precede action <i>C</i>.<p><a name="22227"></a><hr><h2>8.3 Rules About Variables</h2>Let <i>T</i><em></em> be a thread and <i>V</i><i></i> be a variable. There are certain constraints on the operations performed by <i>T</i><em></em> with respect to <i>V  </i><i></i>:<p><ul><li>A <i>use</i> or <i>assign</i> by <i>T</i><em></em> of <i>V</i><i></i> is permitted only when dictated by execution by <i>T</i><em></em> of the program according to the standard execution model. For example, an occurrence of <i>V</i><i></i> as an operand of the <code>+</code> operator requires that a single <i>use</i> operation occur on <i>V</i><i>  </i>; an occurrence of <i>V</i><i></i> as the left-hand operand of the assignment operator <code>=</code> requires that a single <i>assign</i> operation occur. All <i>use</i> and <i>assign</i> actions by a given thread must occur in the order specified by the program being executed by the thread. If the following rules forbid <i>T</i><em></em> to perform a required <i>use</i> as its next action, it may be necessary for <i>T</i><em></em> to perform a <i>load </i>first in order to make progress.<p><li>A <i>store</i> operation by <i>T</i><em></em> on <i>V</i><i></i> must intervene between an <i>assign</i> by <i>T</i><em></em> of <i>V</i><i></i> and a subsequent <i>load</i> by <i>T</i><em></em> of <i>V</i><i></i>. (Less formally: a thread is not permitted to lose the most recent assign.)<p><li>An <i>assign</i> operation by <i>T</i><em></em> on <i>V</i><i></i> must intervene between a <i>load</i> or <i>store</i> by <i>T</i><em></em> of <i>V</i><i></i> and a subsequent <i>store</i> by <i>T</i><em></em> of <i>V</i><i></i>. (Less formally: a thread is not permitted to write data from its working memory back to main memory for no reason.)<p><li>After a thread is created, it must perform an <i>assign</i> or <i>load</i> operation on a variable before performing a <i>use</i> or <i>store</i> operation on that variable. (Less formally: a new thread starts with an empty working memory.)<p><li>After a variable is created, every thread must perform an <i>assign</i> or <i>load</i> operation on that variable before performing a <i>use</i> or <i>store</i> operation on that variable. (Less formally: a new variable is created only in main memory and is not initially in any thread's working memory.)</ul><a name="22234"></a>Provided that all the constraints in Sections <a href="Threads.doc.html#22227">8.3</a>, <a href="Threads.doc.html#22253">8.6</a>, and <a href="Threads.doc.html#22258">8.7</a> are obeyed, a <i>load</i> or <i>store</i> operation may be issued at any time by any thread on any variable, at the whim of the implementation.<p><a name="22235"></a>There are also certain constraints on the <i>read</i> and <i>write</i> operations performed by main memory:<p><ul><li>For every <i>load</i> operation performed by any thread <em>T</em> on its working copy of a variable <i>V</i><i></i>, there must be a corresponding preceding <i>read</i> operation by the main memory on the master copy of <i>V</i><i></i>, and the <i>load</i> operation must put into the working copy the data transmitted by the corresponding <i>read</i> operation.<p><li>For every <i>store</i> operation performed by any thread <em>T</em> on its working copy of a variable <i>V</i><i></i>, there must follow a corresponding <i>write</i> operation by the main memory on the master copy of <i>V</i><i></i>, and the <i>write</i> operation must put into the master copy the data transmitted by the corresponding <i>store</i> operation.<p><li>Let action <i>A</i> be a <i>load</i> or <i>store</i> by thread <em>T</em> on variable <i>V</i><i></i>, and let action <i>P</i> be the corresponding <i>read</i> or <i>write</i> by the main memory on variable <i>V</i><i></i>. Similarly, let action <i>B</i><i> </i>be some other <i>load</i> or <i>store</i> by thread <em>T</em> on that same variable <i>V</i><i></i>, and let action <i>Q</i> be the corresponding <i>read</i> or <i>write</i> by the main memory on variable <i>V</i><i></i>. If <i>A</i> precedes <i>B</i>, then <i>P</i> must precede <i>Q</i>. (Less formally: operations on the master copy of any given variable on behalf of a thread are performed by the main memory in exactly the order that the thread requested.)</ul>Note that this last rule applies <i>only</i> to actions by a thread on the <i>same</i> variable. However,there is a more stringent rule for <code>volatile</code> variables <a href="Threads.doc.html#22258">(§8.7)</a>.<p><a name="22244"></a><hr><h2>8.4 Nonatomic Treatment of <font size=+4><code>double</code></font> and <font size=+4><code>long</code></font> Variables</h2>If a <code>double</code> or <code>long</code> variable is not declared <code>volatile</code>, then for the purposes of <i>load</i>, <i>store</i>, <i>read</i>, and <i>write</i> operations it is treated as if it were two variables of 32 bits each; wherever the rules require one of these operations, two such operationsare performed, one for each 32-bit half. The manner in which the 64 bits of a <code>double</code> or <code>long</code> variable are encoded into two 32-bit quantities and the order of the operations on the halves of the variables are not defined by <i>The Java</i><sup></sup><i> Language Specification</i>. <p><a name="22246"></a>This matters only because a <i>read</i> or <i>write</i> of a <code>double</code> or <code>long</code> variable may be handled by an actual main memory as two 32-bit <i>read</i> or <i>write</i> operations that may be separated in time, with other operations coming between them. Consequently, if two threads concurrently assign distinct values to the same shared non-<code>volatile</code> <code>double</code> or <code>long</code> variable, a subsequent use of that variable may obtain a value that is not equal to either of the assigned values, but rather some implementation-dependent mixture of the two values.<p><a name="25546"></a>An implementation is free to implement <i>load</i>, <i>store</i>, <i>read</i>, and <i>write</i> operations for <code>double</code> and <code>long</code> values as atomic 64-bit operations; in fact, this is strongly encouraged. The model divides them into 32-bit halves for the sake of currently popular microprocessors that fail to provide efficient atomic memory transactions on 64-bit quantities. It would have been simpler for the Java virtual machine to define all memory transactions on single variables as atomic; this more complex definition is a pragmatic concession to current hardware practice. In the future this concession may be eliminated. Meanwhile, programmers are cautioned to explicitly synchronize access to shared <code>double</code> and <code>long</code> variables.<p><a name="25549"></a><hr><h2>8.5 Rules About Locks</h2>Let <i>T</i><em></em> be a thread and <i>L</i> be a lock. There are certain constraints on the operations performed by <i>T</i><em></em> with respect to <i>L</i>:<p><ul><li>A <i>lock</i> operation by <i>T</i><em></em> on <i>L</i> may occur only if, for every thread <i>S</i><i></i> other than <i>T</i><em></em>, the number of preceding <i>unlock</i> operations by <i>S</i><i></i> on <i>L</i> equals the number of preceding <i>lock</i> operations by <i>S</i><i></i> on <i>L</i>. (Less formally: only one thread at a time is permitted to lay claim to a lock; moreover, a thread may acquire the same lock multiple times and does not relinquish ownership of it until a matching number of <i>unlock</i> operations have been performed.)<p><li>An <i>unlock</i> operation by thread <i>T</i><em></em> on lock <i>L</i> may occur only if the number of preceding <i>unlock</i> operations by <i>T</i><em></em> on <i>L</i> is strictly less than the number of preceding <i>lock</i> operations by <i>T</i><em></em> on <i>L</i>. (Less formally: a thread is not permitted to unlock a lock it does not own.)</ul>With respect to a lock, the <i>lock</i> and <i>unlock</i> operations performed by all the threads are performed in some total sequential order. This total order must be consistent with the total order on the operations of each thread.<p><a name="22253"></a><hr><h2>8.6 Rules About the Interaction of Locks and Variables</h2>Let <i>T</i><em></em> be any thread, let <i>V</i><i></i> be any variable, and let <i>L</i> be any lock. There are certain constraints on the operations performed by <i>T</i><em></em> with respect to <i>V</i><i></i> and <i>L</i>:<p><ul><li>Between an <i>assign</i> operation by <i>T</i><em></em> on <i>V</i><i></i> and a subsequent <i>unlock</i> operation by <i>T</i><em></em> on <i>L</i>, a <i>store</i> operation by <i>T</i><em></em> on <i>V</i><i></i> must intervene; moreover, the <i>write</i> operation corresponding to that <i>store</i> must precede the <i>unlock</i> operation, as seen by main memory. (Less formally: if a thread is to perform an <i>unlock</i> operation on <i>any</i> lock, it must first copy <i>all</i> assigned values in its working memory back out to main memory.)<p><li>Between a <i>lock</i> operation by <i>T</i><em></em> on <i>L</i> and a subsequent <i>use</i> or <i>store</i> operation by <i>T</i><em></em> on a variable <i>V</i><i></i>, an <i>assign</i> or <i>load</i> operation on <i>V</i> must intervene; moreover, if it is a <i>load</i> operation, then the <i>read</i> operation corresponding to that <i>load</i> must follow the <i>lock</i> operation, as seen by main memory. (Less formally: a <i>lock</i> operation behaves as if it flushes <i>all</i> variables from the thread's working memory, after which the thread must either assign them itself or load copies anew from main memory.)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -