📄 threads.doc.html
字号:
<li>A <i>lock</i> operation by T<em></em> on L<i></i> may occur only if, for every thread S<i></i> other than T<em></em>, the number of preceding <i>unlock</i> operations by S<i></i> on L<i></i> equals the number of preceding <i>lock</i> operations by S<i></i> on L. (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.)
<a name="22251"></a>
<li>An <i>unlock</i> operation by thread T<em></em> on lock L<i></i> may occur only if the number of preceding <i>unlock</i> operations by T<em></em> on L<i></i> is strictly less than the number of preceding <i>lock</i> operations by T<em></em> on L<i></i>. (Less formally: a thread is not permitted to unlock a lock it does not own.)
</ul><a name="22252"></a>
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>
<a name="22254"></a>
Let T<em></em> be any thread, let V<i></i> be any variable, and let L<i></i> be any lock. There are certain
constraints on the operations performed by T<em></em> with respect to V<i></i> and L<i></i>:
<p><ul><a name="22255"></a>
<li>Between an <i>assign</i> operation by T<em></em> on V<i></i> and a subsequent <i>unlock</i> operation by T<em></em> on L<i></i>, a <i>store</i> operation by T<em></em> on V<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.)
<a name="22256"></a>
<li>Between a <i>lock</i> operation by T<em></em> on L<i></i> and a subsequent <i>use</i> or <i>store</i> operation by T<em></em> on a variable V<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 it must either assign them itself or load copies anew from main memory.)
</ul><a name="22258"></a>
<hr><h2>8.7 Rules for Volatile Variables</h2>
<a name="22259"></a>
If a variable is declared volatile, then additional constraints apply to the operations of each thread. Let T<em></em> be a thread and let V<i></i> and W be volatile variables.
<p><ul><a name="22260"></a>
<li>A <i>use</i> operation by T<em></em> on V<i></i> is permitted only if the previous operation by T<em></em> on V<i></i> was <i>load</i>, and a <i>load</i> operation by T<em></em> on V<i></i> is permitted only if the next operation by T<em></em> on V<i></i> is <i>use</i>. The <i>use</i> operation is said to be "associated" with the <i>read</i> operation that corresponds to the <i>load</i>.
<a name="22261"></a>
<li>A <i>store</i> operation by T<em></em> on V<i></i> is permitted only if the previous operation by T<em></em> on V<i></i> was <i>assign</i>, and an <i>assign</i> operation by T<em></em> on V<i></i> is permitted only if the next operation by T<em></em> on V<i></i> is <i>store</i>. The <i>assign</i> operation is said to be "associated" with the <i>write</i> operation that corresponds to the <i>store</i>.
<a name="22262"></a>
<li>Let action A be a <i>use</i> or <i>assign</i> by thread T<em></em> on variable V<i></i>, let action F be the <i>load</i> or <i>store</i> associated with A, and let action P be the <i>read</i> or <i>write</i> of V<i></i> that corresponds to F. Similarly, let action B be a <i>use</i> or <i>assign</i> by thread T<em></em> on variable W<i></i>, let action G be the <i>load</i> or <i>store</i> associated with B, and let action Q be the <i>read</i> or <i>write</i> of V<i></i> that corresponds to G<i></i>. If A precedes B, then P must precede Q. (Less formally: operations on the master copies of volatile variables on behalf of a thread are performed by the main memory in exactly the order that the thread requested.)
</ul><a name="24432"></a>
<hr><h2>8.8 Prescient Store Operations</h2>
<a name="24433"></a>
If a variable is not declared <code>volatile</code>, then the rules in the previous sections are
relaxed slightly to allow <i>store</i> operations to occur earlier than would otherwise be
permitted. The purpose of this relaxation is to allow optimizing Java compilers to
perform certain kinds of code rearrangement that preserve the semantics of properly synchronized programs, but might be caught in the act of performing memory
operations out of order by programs that are not properly synchronized.
<p><a name="24434"></a>
Suppose that a <i>store</i> by T of V would follow a particular <i>assign</i> by T of V according to the rules of the previous sections, with no intervening <i>load</i> or <i>assign</i> by T of V. Then that <i>store</i> operation would send to the main memory the value that the <i>assign</i> operation put into the working memory of thread T. The special rule allows the <i>store</i> operation actually to occur before the <i>assign</i> operation instead, if the following restrictions are obeyed:<p>
<ul><a name="24435"></a>
<li>If the <i>store</i> operation occurs, the <i>assign</i> is bound to occur. (Remember, these are restrictions on what actually happens, not on what a thread plans to do. No fair performing a <i>store</i> and then throwing an exception before the <i>assign</i> occurs!)
<a name="24436"></a>
<li>No <i>lock</i> operation intervenes between the relocated <i>store</i> and the <i>assign</i>.
<a name="24437"></a>
<li>No <i>load</i> of V intervenes between the relocated <i>store</i> and the <i>assign</i>.
<a name="24438"></a>
<li>No other <i>store</i> of V intervenes between the relocated <i>store</i> and the <i>assign</i>.
<a name="24439"></a>
<li>The <i>store</i> operation sends to the main memory the value that the <i>assign</i> operation will put into the working memory of thread T.
</ul><a name="24440"></a>
This last property inspires us to call such an early <i>store</i> operation <i>prescient</i>: it has
to know ahead of time, somehow, what value will be stored by the <i>assign</i> that it
should have followed. In practice, optimized compiled code will compute such
values early (which is permitted if, for example, the computation has no side
effects and throws no exceptions), store them early (before entering a loop, for
example), and keep them in working registers for later use within the loop.
<p><a name="22263"></a>
<hr><h2>8.9 Discussion</h2>
<a name="22264"></a>
Any association between locks and variables is purely conventional. Locking
any lock conceptually flushes <i>all</i> variables from a thread's working memory,
and unlocking any lock forces the writing out to main memory of <i>all</i> variables
that the thread has assigned. That a lock may be associated with a particular
object or a class is purely a convention. In some applications, it may be appropriate always to lock an object before accessing any of its instance variables, for
example; <code>synchronized</code> methods are a convenient way to follow this convention. In other applications, it may suffice to use a single lock to synchronize
access to a large collection of objects.
<p><a name="22265"></a>
If a thread uses a particular shared variable only after locking a particular lock and before the corresponding unlocking of that same lock, then the thread will read the shared value of that variable from main memory after the <i>lock</i> operation, if necessary, and will copy back to main memory the value most recently assigned to that variable before the <i>unlock</i> operation. This, in conjunction with the mutual exclusion rules for locks, suffices to guarantee that values are correctly transmitted from one thread to another through shared variables.<p>
<a name="22266"></a>
The rules for volatile variables effectively require that main memory be touched exactly once for each <i>use</i> or <i>assign</i> of a volatile variable by a thread, and that main memory be touched in exactly the order dictated by the thread execution semantics. However, such memory operations are not ordered with respect to <i>read</i> and <i>write</i> operations on nonvolatile variables.<p>
<a name="23865"></a>
<hr><h2>8.10 Example: Possible Swap</h2>
<a name="23866"></a>
Consider a class that has class variables <code>a</code> and <code>b</code> and methods <code>hither</code> and <code>yon</code>:
<p><pre><br><a name="23867"></a> class Sample {
</pre><pre> int a = 1, b = 2;
void hither() {
a = b;
}
void yon()
b = a;
}
<a name="23875"></a> }
<br></pre><a name="23876"></a>
Now suppose that two threads are created, and that one thread calls <code>hither</code> while
the other thread calls <code>yon</code>. What is the required set of actions and what are the
ordering constraints?
<p><a name="23877"></a>
Let us consider the thread that calls <code>hither</code>. According to the rules, this thread must perform a <i>use</i> of <code>b</code> followed by an <i>assign</i> of <code>a</code>. That is the bare minimum required to execute a call to the method <code>hither</code>.<p>
<a name="23878"></a>
Now, the first operation on variable <code>b</code> by the thread cannot be <i>use</i>. But it may be <i>assign</i> or <i>load</i>. An <i>assign</i> to <code>b</code> cannot occur because the program text does not call for such an <i>assign</i> operation, so a <i>load</i> of <code>b</code> is required. This <i>load</i> operation by the thread in turn requires a preceding <i>read</i> operation for <code>b</code> by the main memory.<p>
<a name="23879"></a>
The thread may optionally <i>store</i> the value of <code>a</code> after the <i>assign</i> has occurred. If it does, then the <i>store</i> operation in turn requires a following <i>write</i> operation for <code>a</code> by the main memory.<p>
<a name="23880"></a>
The situation for the thread that calls <code>yon</code> is similar, but with the roles of <code>a</code> and <code>b</code> exchanged.<p>
<a name="23908"></a>
The total set of operations may be pictured as follows:<br><br><img src="Threads.doc.anc.gif">
<br><br><p>
<a name="23909"></a>
Here an arrow from action A to action B indicates that A must precede B.
<p><a name="24312"></a>
In what order may the operations by the main memory occur? The only constraint is that it is not possible both for the <i>write</i> of <code>a</code> to precede the <i>read</i> of <code>a</code> and for the <i>write</i> of <code>b</code> to precede the <i>read</i> of <code>b</code>, because the causality arrows in the diagram would form a loop so that an action would have to precede itself, which is not allowed. Assuming that the optional <i>store</i> and <i>write</i> operations are to occur, there are three possible orderings in which the main memory might legitimately perform its operations. Let <code>ha</code> and <code>hb</code> be the working copies of <code>a</code> and <code>b</code> for the <code>hither</code> thread, let <code>ya</code> and <code>yb</code> be the working copies for the <code>yon</code> thread, and let <code>ma</code> and <code>mb</code> be the master copies in main memory. Initially <code>ma=1</code> and <code>mb=2</code>. Then the three possible orderings of operations and the resulting states are as follows:<p>
<ul><a name="24313"></a>
<li><i>write</i> <code>a</code><code><img src="chars/arrwrite.gif"></code><i>read</i> <code>a</code>, <i>read</i> <code>b</code><code><img src="chars/arrwrite.gif"></code><i>write</i> <code>b</code> (then <code>ha=2</code>, <code>hb=2</code>, <code>ma=2</code>, <code>mb=2</code>, <code>ya=2</code>, <code>yb=2</code>)
<a name="24325"></a>
<li><i>read</i> <code>a</code><code><img src="chars/arrwrite.gif"></code><i>write</i> <code>a</code>, <i>write</i> <code>b</code><code><img src="chars/arrwrite.gif"></code><i>read</i> <code>b</code> (then <code>ha=1</code>, <code>hb=1</code>, <code>ma=1</code>, <code>mb=1</code>, <code>ya=1</code>, <code>yb=1</code>)
<a name="24326"></a>
<li><i>read</i> <code>a</code><code><img src="chars/arrwrite.gif"></code><i>write</i> <code>a</code>, <i>read</i> <code>b</code><code><img src="chars/arrwrite.gif"></code><i>write</i> <code>b</code> (then <code>ha=2</code>, <code>hb=2</code>, <code>ma=2</code>, <code>mb=1</code>, <code>ya=1</code>, <code>yb=1</code>)
</ul><a name="24327"></a>
Thus, the net result might be that, in main memory, <code>b</code> is copied into <code>a</code>, <code>a</code> is copied
into <code>b</code>, or the values of <code>a</code> and <code>b</code> are swapped; moreover, the working copies of the
variables might or might not agree. It would be incorrect, of course, to assume that
any one of these outcomes is more likely than another. This is one place in which
the behavior of a Java program is necessarily timing-dependent.
<p><a name="23915"></a>
Of course, an implementation might also choose not to perform the <i>store</i> and <i>write</i> operations, or only one of the two pairs, leading to yet other possible results.<p>
<a name="23916"></a>
Now suppose that we modify the example to use <code>synchronized</code> methods:<p>
<pre><br><a name="23917"></a> class SynchSample {
</pre><pre> int a = 1, b = 2;
synchronized void hither() {
a = b;
}
synchronized void yon()
b = a;
}
<a name="23925"></a> }
<br></pre><a name="23926"></a>
Let us again consider the thread that calls <code>hither</code>. According to the rules, this
thread must perform a <i>lock</i> operation (on the <code>Class</code> object for class <code>SynchSample</code>)
before the body of method <code>hither</code> is executed. This is followed by a <i>use</i> of <code>b</code> and
then an <i>assign</i> of <code>a</code>. Finally, an <i>unlock</i> operation on the <code>Class</code> object must be performed after the body of method <code>hither</code> completes. That is the bare minimum
required to execute a call to the method <code>hither</code>.
<p><a name="23927"></a>
As before, a <i>load</i> of <code>b</code> is required, which in turn requires a preceding <i>read</i> operation for <code>b</code> by the main memory. Because the <i>load</i> follows the <i>lock</i> operation, the corresponding <i>read</i> must also follow the <i>lock</i> operation.<p>
<a name="23928"></a>
Because an <i>unlock</i> operation follows the <i>assign</i> of <code>a</code>, a <i>store</i> operation on <code>a</code> is mandatory, which in turn requires a following <i>write</i> operation for <code>a</code> by the main memory. The <i>write</i> must precede the <i>unlock</i> operation.<p>
<a name="23929"></a>
The situation for the thread that calls <code>yon</code> is similar, but with the roles of <code>a</code> and <code>b</code> exchanged.<p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -