?? statebased.html
字號:
<html><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><html> <head><title>State-Based Notifications</title></head><BODY bgcolor=#ffffee vlink=#0000aa link=#cc0000><body><h1>State-Based Notifications</h1>As a default, conservative strategy for dealing with synchronization,you might place <code>notifyAll</code> calls every time you change thevalue of any instance variable that's mentioned in any<code>wait</code> condition. But you can almost always do better thanthis (in terms of efficiency, understandability, and design elegance)by associating synchronization mechanics with the high-level<em>logical</em> state of an object.<p> The idea is that instead of dealing with particular values andvariables, <code>waits</code> should deal with logical stateconditions. And similarly, when any operation causes a logical state<em>change</em>, a notification is issued. The technique is applicablewhenever you can partition the different behaviors of an object interms of a finite (usually small) number of logical states.<p> For example, in the <ahref="synchDesign.html#secCounter" tppabs="http://www.foi.hr/~dpavlin/java/mirrors/g.oswego.edu/dl/pats/synchDesign.html#secCounter">BoundedCounter</a> class, we canidentify three logical states, along with the behavior of<CODE>inc</CODE> and <CODE>dec</CODE> associated with these states:<PRE>state condition inc dec-------------------------------------------------------bottom count == minVal go waitmiddle minVal < count < maxVal go gotop count == maxVal wait go</PRE><p>There are two ways to go from here, using either <em>explicit</em>or <em>implicit</em> state tracking.<h2>Explicit State Tracking</h2>Logical state can be encoded in an instance variable, and maintainedby a method that issues notifications only upon state<EM>changes</EM>. The <CODE>wait</CODE> methods should then bechanged to wait for appropriate values of the state variable. Forexample:<PRE>class BoundedCounterVS implements BoundedCounter { static final int bottomState = 0; static final int middleState = 1; static final int topState = 2; public synchronized int value() { return count_; } public synchronized void inc() { waitUntilIncrementable(); ++count_; checkState(); } public synchronized void dec() { waitUntilDecrementable(); --count_; checkState(); } public BoundedCounterVS() { count_ = minValue; myState_ = bottomState; } private int count_; private int myState_; private synchronized void waitUntilIncrementable() { while (myState_ == topState) try { wait(); } catch(InterruptedException ex) {}; } private synchronized void waitUntilDecrementable() { while (myState_ == bottomState) try { wait(); } catch(InterruptedException ex) {}; } private synchronized void checkState() { int oldState = myState_; if (count_ == minVal) myState_ = bottomState; else if (count_ == maxVal) myState_ = topState; else myState_ = middleState; if (myState_ != oldState) notifyAll(); }}</PRE>This code will generate far fewer notifications than the originalversion in which <em>every</em> change to <code>count_</code> caused anotification. Since <code>notifyAll</code> is a relatively expensiveoperation in Java, this version will probably run faster.<h2>Implicit Tracking</h2>With implicit tracking, representations of logical state are encodeddirectly into the methods performing the waiting and notification.<p>For example, in <CODE>BoundedCounter</CODE>, the only changes thatcould possibly affect waiting threads are those that step<CODE>count_</CODE> away from <CODE>minVal</CODE> and<CODE>maxVal</CODE>. Thus, the <CODE>inc()</CODE> method need onlynotify if, before the increment, the <CODE>count_ == minVal</CODE>,and conversely for <CODE>dec()</CODE>, as in:<PRE> public synchronized void inc() { waitUntilIncrementable(); ++count_; if (count_ -1 == minVal) notifyAll(); } public synchronized void dec() { waitUntilDecrementable(); --count_; if (count_ + 1 == maxVal) notifyAll(); } }</PRE><h3>Comparison</h3><p> Explicit state tracking is almost always preferable to implicittracking. While it can sometimes be made to generate fewernotifications, implicit tracking pays a much greater penalty offragility and inflexibility.<p> For example, here, the implicit version does not bother generating<code>notifyAll</code> when it steps up <em>to</em> the <code>count_== maxVal</code> state since there turns out not to be any actionassociated with that transition. The explicit version does thenotification anyway, blind to the fact that there don't happento be any ``consumers'' of this logical state change.<p> However, it would be impossible to build subclasses of theimplicit version that include methods with guards waiting for anyother property of <CODE>count_</CODE> than the particular ones sensedin the <code>inc()</code> and <code>dec()</code> methods withoutcompletely rewriting the methods. (Granted, this would not be a bigdeal here, but would be if we were not using toy examples forillustration!). This is another example of the ``inheritanceanomaly''.<p> In contrast the explicit version contains a good deal moreflexibility. Subclasses can subdivide the logical states; forexample, they could add explicit state <code>nextToBottomState</code>if there were something special that needed to be done when the countis exactly one. Similarly, because all of the notification code isencapsulated within <code>checkState</code>, subclasses can overrideit to refine it in ways that are more specialized yet still consistentwith the code that relies upon it.<h2>State Objects</h2>One can go further down along the explicit state tracking route byusing the ``States as Objects'' pattern (See the <AHREF="javascript:if(confirm('http://st-www.cs.uiuc.edu/users/patterns/Books.html \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address. \n\nDo you want to open it from the server?'))window.location='http://st-www.cs.uiuc.edu/users/patterns/Books.html'" tppabs="http://st-www.cs.uiuc.edu/users/patterns/Books.html"> DesignPatterns</A> book or <ahref="javascript:if(confirm('http://g.oswego.edu/dl/oosdw3/ch19/ch19.html \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address. \n\nDo you want to open it from the server?'))window.location='http://g.oswego.edu/dl/oosdw3/ch19/ch19.html'" tppabs="http://g.oswego.edu/dl/oosdw3/ch19/ch19.html"> OOSD</a>.) Here,rather than encoding the state as a variable, you encode it as areference to an object that behaves <em>only</em> as the main objectwould if it were in the associated state. This strategy is useful (andworth its set-up overhead in terms of associated helper classes) whennot only the wait conditions, but also the other action code variesacross states. This is not the case for the BoundedCounter examplebut (<em>to be continued</em>).<p>For some further variants and extensions to this pattern, see papersby <a href="javascript:if(confirm('http://choices.cs.uiuc.edu/sane/home.html \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address. \n\nDo you want to open it from the server?'))window.location='http://choices.cs.uiuc.edu/sane/home.html'" tppabs="http://choices.cs.uiuc.edu/sane/home.html"> AamodSane</a>).<p><a href="aopintro.html" tppabs="http://www.foi.hr/~dpavlin/java/mirrors/g.oswego.edu/dl/pats/aopintro.html">[Concurrent Programming in Java]</a><hr><address><A HREF="javascript:if(confirm('http://g.oswego.edu/dl \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address. \n\nDo you want to open it from the server?'))window.location='http://g.oswego.edu/dl'" tppabs="http://g.oswego.edu/dl">Doug Lea</A></address><!-- hhmts start -->Last modified: Tue Feb 20 06:29:00 EST 1996<!-- hhmts end --></body> </html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -