📄 synchdesign.html
字号:
<html><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><html> <head><title>Designing Objects for Concurrency</title></head><BODY bgcolor=#ffffee vlink=#0000aa link=#cc0000><h1>Designing Objects for Concurrency</h1>Objects within multithreaded Java programs interact. Interactingobjects involved in multiple threads require synchronization (unlessthey happen to be <a href="immut.html" tppabs="http://www.foi.hr/~dpavlin/java/mirrors/g.oswego.edu/dl/pats/immut.html">immutable</a>). But piecemealapproaches to object synchronization do not scale well. Instead ofadding synchronization constructs here and there to sequentialdesigns, it is a much better idea to start out thinking about the<em>logical</em> conditions or <em>guards</em> under which each methodyou write may execute in a concurrent setting. If every objectperforms actions only when it is safe to do so, and the mechanics areproperly implemented with Java synchronization mechanisms, then you canbe sure that an application using these objects will not encounter anyerrors due to object inconsistency. <p>This practice reflects a design policy that <P><strong> It is better to do nothing at all than to do something that compromises object integrity.</strong><P> Another way of saying this is that whenever there is a choice, itis a much better bet to choose <EM>SAFETY</EM> -- the property thatnothing bad happens, over <EM>LIVENESS</EM> -- the property thatanything happens at all.<p>According to this view, it is evenpreferable to deadlock a set of objects (have them each wait for eachother in an endless chain) than to have them mutually interfere witheach other, leaving one or more of them in inconsistent states, thuscausing them to later take random, even dangerous actions. This stanceis especially attractive considering that clients that do not want toencounter the delays of unknown duration associated with guardedmethods can always spawn new <AHREF="javascript:if(confirm('http://g.oswego.edu/dl/pats/javaconc.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/pats/javaconc.html'" tppabs="http://g.oswego.edu/dl/pats/javaconc.html">Threads</A> to waitthem out. Implementations are generally obligated to make good onthis policy by preventing actions from occurring when objects areunable to perform them, and conversely, actually performing them whenthey are able.<p> This sense of safety is a distinguishing characteristic ofreusable concurrent classes versus one-shot application classes. Inone-shot settings, you may know enough about the context that anobject is used in to not bother thinking through the consequences ofarbitrary concurrency. But when you are constructing the buildingblocks for entire suites of applications or systems, you cannot affordto ignore such issues. And even for one-shot applications, theconsequences of the lack of proper synchronization are very difficultto predict and time-consuming to diagnose in a buggy program. So, ifnothing else, planning for concurrency ahead of time is usually moretime-effective than trying to deal with problems after the fact.(However, for ways to rescue sequential designs, see especially,the <a href="coordinators.html" tppabs="http://www.foi.hr/~dpavlin/java/mirrors/g.oswego.edu/dl/pats/coordinators.html"> Coordinator</a> pattern.)<P><H3><A NAME="secCounter">Bounded Counter</H3>Here is a variation of a classic <EM>counting semaphore</EM>, thatwill be used as the main running example for describing designs anddesign patterns for objects with guarded actions . It is definedusing an <CODE>interface</CODE> to provide scaffolding for differentimplementations:<PRE>interface BoundedCounter { public static final int minVal = 0; // minimum allowed value() public static final int maxVal = 10; // maximum allowed value() public int value(); // invariant: minVal <= value() <= maxVal public void inc(); // increment only when value() < maxVal public void dec(); // decrement only when value() > minVal}</PRE><p> To distinguish design-level concerns from implementation issues,we'll use a shorthand pidgin-java notation to express guards, placingthem in double square brackets. Using guards, an abstract implementationclass would look like:<PRE>class BoundedCounterV0 implements BoundedCounter { public synchronized int value() { return count_; } public synchronized void inc() [[ when count_ < maxVal ]] { ++count_; } public synchronized void dec() [[ when count_ > minVal ]] { --count_; } public BoundedCounterV0( { count_ = minValue; } private int count_; }</PRE>The idea here is that the <CODE>BoundedCounter </CODE> is obligated tokeep <CODE>count</CODE> between <CODE>minVal</CODE> and<CODE>maxVal</CODE>. So it can only increment or decrement the<CODE>count</CODE> when it is within these bounds. In a purelysequential program, you might program this to raise an exception if a<CODE>dec</CODE> message is received when the <CODE>count_</CODE> is<CODE>minVal</CODE>. But in a concurrent setting, we instead want topostpone any action, so that it may occur sometime later if and whenthe <CODE>count</CODE> becomes greater than <CODE>minVal</CODE> via an<CODE>inc</CODE> message. <P>While this example is pretty boring, it is conveniently simple forillustrating techniques, and turns out to be a good model for severalcommon concurrent object designs. For example, you mightthink it more interesting and relevant if you think of<CODE>dec</CODE> and <CODE>inc</CODE> as standins for methods thatgive out and take back a fixed number of resources rather than justcounting their use, or for those in a classic fixed-length buffer: <P><PRE>interface BoundedBuffer { public static final int CAPACITY = 10; public int count(); // invariant: 0 <= count() <= CAPACITY public void put(Object x); // add only when count() < CAPACITY public Object take(); // remove only when count() > 0}</PRE>Designs for BoundedCounter can be transformed into those forBoundedBuffer by translating:<ul> <li> minVal -- 0 <li> maxVal -- CAPACITY <li> value -- count <li> inc -- put (adding code to actually place object in an array slot) <li> dec -- take (adding code to remove and return object).</ul>Also, there's no real reason except simplicity of examples to require<code>minVal</code> and <code>maxVal</code> to be <code>static</code>constants. In more realistic versions, these might be per-objectconstants, established in constructors.<H2><A NAME="secGuardMechanics"></A>Implementing Guards</H2>The standard coding idiom for expressing guarded waits is a simple<CODE>while</CODE> loop. To help ensure that this is always donecorrectly, it is good practice to encapsulate it in its own non-publicmethod: <P>Guarded Form:<PRE> [[ when cond ]] { action(); }</PRE>Java:<PRE> private synchronized void waitUntilCond() { while (!cond) try { wait(); } catch (InterruptedException ex) {} } ... { waitUntilCond(); action(); }</PRE><p>As a general rule, condition waits should always be placed in while
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -