USENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 1© 2003 David Holmes and Doug Lea1Introduction to ConcurrentProgramming in Java™(lots of slides cut for 433)David HolmesDLTeCH Pty LtdBrisbane, [email protected] LeaState Universityof New YorkOswego, [email protected]://gee.cs.oswego.edu/~dl/© 2003 David Holmes and Doug Lea2Designing Objects for Concurrency Isolation Avoiding interference by not sharing Immutability Avoiding interference by avoiding change Locking Dynamically guaranteeing exclusive access Splitting objects Changing representation to facilitate concurrency control Containment Guaranteeing exclusive control of internal components Managing ownership Protecting unhidden components Alternatives to synchronization volatiles and the Java Memory ModelUSENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 2© 2003 David Holmes and Doug Lea3Isolation Objects that are not shared can not suffer interference Heap objects accessible only from current thread Parameters and local variables Applies to references not the objects referred to java.lang.ThreadLocal Simplifies access from other objects running in same thread No need for any synchronization Objects can be shared across threads provided they are isolated toone thread at a time Transfer of ownership protocols T1 uses O1, hands off to T2 and then forgets about O1 Transfer requires synchronization—subsequent use of objectdoes not© 2003 David Holmes and Doug Lea4Thread Locals Suppose you want multiple web servers, each running in a differentthread, and each using a different document directory Could define a documentRoot field in WebServer class Or, define the document root as a variable tied to the Thread Easiest way to do this is to use java.lang.ThreadLocal Equivalent to adding instance variables to all Threadobjects No need to define subclasses or control thread creation All methods running in the thread can access when needed ThreadLocals are often package accessible statistics No interference when ALL access is within same threadpublic class WebServer {static final ThreadLocal documentRoot = new ThreadLocal();// ...public WebServer(int port, File root) throws IOException { // ... documentRoot.set(root);}private void processRequest(Socket sock)throws IOException File root = (File) documentRoot.get(); // ...}USENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 3© 2003 David Holmes and Doug Lea5When to Use Thread Locals Variables that apply per-activity, not per-object Timeout values, transaction IDs, Principals, currentdirectories,default parameters Replacements for static variables When different threads should use different values Tools to eliminate need for locking Used internally in JVMs to optimize memory allocation, locks,etc via per-thread caches© 2003 David Holmes and Doug Lea6Stateless Objectsclass StatelessAdder {int addOne( int i) { return i + 1; }int addTwo( int i) { return i + 2; }} There are no special concurrency concerns No storage conflicts as no per-instance state No representation invariants as no representation Multiple concurrent executions—so no liveness problems No need to create threads to make this call No interaction with other objects—so no concurrent protocoldesign issues Example: java.lang.MathUSENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 4© 2003 David Holmes and Doug Lea7Immutable Objectsclass ImmutableAdder { private final int offset; // blank final ImmutableAdder(int offset) { this.offset = offset; } int add(int i) { return i + offset; }} Object state frozen upon initialisation Still no safety or liveness concerns No interference as per-instance state never changes Java blank finals enforce most senses of immutability Immutability is often suitable for closed Abstract Data Types eg. java.lang.String, java.lang.Integer© 2003 David Holmes and Doug Lea8Containment of Unsafe Objects Suppose Statistics class was written as follows: public static class Statistics { // Mutable! public long requests; public double avgTime; public Statistics(long requests, double avgTime) { this.requests = requests; this.avgTime = avgTime; } } Fields are public and mutable! Therefore instances can not be shared Can be safely contained within a WebServer instance private final Statistics stats = new Statistics(0,0.0);public synchronized Statistics getStatistics() { return new Statistics(stats.requests, stats.avgTime);}private void processRequest(Socket sock) throws IOException { // .... synchronized(this) { double total = stats.avgTime*stats.requests + elapsed; stats.avgTime = total / (++stats.requests); }}Can’t expose mutable state so we make copies of itUSENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 5© 2003 David Holmes and Doug Lea9Containment Strict containment creates islands of objects Applies recursively Allows inner code to run faster Can be used with legacy sequential code Requires inner code to be communication closed No unprotected calls into or out of island Requires outer objects to never leak inner references Or uses ownership transfer protocol Can be difficult to enforce and check© 2003 David Holmes and Doug Lea10Hierarchical Containment Locking Applies when logically contained parts are not hidden from clients Avoids deadlocks that could occur if parts fully synchronised All parts use lock provided by the common owner Can use either internal or external conventionsUSENIX COOTS '98 April 27, 1998Designing Concurrent Object-Oriented Programsin Java 6© 2003 David Holmes and Doug Lea11Internal Containment Locking Visible components protect themselves using their owners’ locksclass Part { protected Container owner_; // Never null public Container owner() {return owner_; } private void bareAction() { /* ... unsafe ... */ } public void m() { synchronized (owner()){ bareAction(); } }} Parts don’t deadlock when invoking each other’s methods Parts must be aware that they are contained Or implement using inner classes—Owner is outer class:class Container{ class Part { //… public void m()( synchronized(Container.this){ bareAction(); } } }} Can extend to
View Full Document