Concurrency in Java (Shooting yourself in the foot)n Acknowledgment This lecture is derived almost exclusively from Java Concurrency in Practice by Goetz, et. al. http://www.javaconcurrencyinpractice.com Additional notes from Dr. Dan Wallach Outline Background Basic Thread Safety Concurrent Object Management Concurrent Library Components Task Execution and Shutdown Next Time Thread Pools GUI Applications Safety and Performance Documentation and Testing Adv. Topics Explicit Locks Custom Synchronizers Nonblocking Synchronization Background Why concurrency? Resource utilization - why wait? Fairness - why wait? Convenience - why wait? First concurrency: processes Later: threads Benefits of Threads Responsiveness (esp. GUIs) Exploiting multi-processors Simplicity of modeling Simplified handling of asynchronous events Risks of Threads Java’s “built-in” threads means that concurrency is NOT an advanced topic Safety hazards (correctness) Liveness hazards (progress) Performance hazards (happiness) Threads are Everywhere Frameworks use threads Timer Servlets and JSPs RMI Swing and AWT You will use this one! Starting a Java Thread public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } } public class HelloThread extends Thread { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new HelloThread()).start(); } } Outline Background Basic Thread Safety Concurrent Object Management Concurrent Library Components Task Execution and Shutdown Synchronization Given a mutable variable v If multiple threads access v AND if one can modify v ALL must synchronize access This includes read access Never attempt to ignore this If sync is broken, so is the code (even if it passes tests) Concurrent Correctness Fixing a broken “shared” var Don’t Share! Or Make the var immutable! Or Synchronize access Better yet, design it right Encapsulation Immutability Clear specifications of invariants What is “Thread-Safe”? Definitions are vague and vary Problem: what is correctness? Thread Safe: “Correct” in multi-threaded env OR, no more broken in a multthreaded environment then in a singlethreaded one Thread-safe classes encapsulate all synchronization Example: Unsafe Counter @NoThreadSafe public class UnsafeSequence{ private int value; public int getNext() { return value++; } } What’s Wrong with That? Invariant: getNext must return a sequence Unlucky execution timing: Value->9 Value->9 9+1->10 Value=10 9+1->10 Value=10 Race Conditions The problem is a race condition Def: r.c. occurs when the correctness of a computation depends on the relative timing of multiple threads by the runtime. Often confused for data race Achieving Safety Go stateless! Use atomic operations Use locking Using Atomics! (It’s not a treaty violation) @ThreadSafe public class UnsafeSequence{ private final AtomicLong value = new AtomicLong(0); public long getNext() { return value.incrementAndGet(); } } Locking Solved one mutable variable by making the var atomic What if we have 2? Can we just make them both atomic? NOT if they are dependent We have to lock any combined operations Example Suppose we have a dictionary that stores the last (key,value) pair Setting the (k,v) pair must be locked. It is not enough to use an atomic var for k, and another one for v Intrinsic Locks Java provides built-in locks Each object is a lock, so is each class Marked by synchronized Enforces Atomicity Memory visibility (more later) Reentrancy Requesting a lock held by another causes a block Intrinsic locks are reentrant A thread that owns a lock, that requests the same lock, will succeed (good for inheritance!) Example Locking @ThreadSafe Public class LockSafe<K,V> { @GuardedBy(“this”) private K key; @GuardedBy(“this”) private V value; public synchronized setKvPair(K k, V v) { this.key = k; this.value = v; } } Locks: “Guarded By” “For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held. In this case, we say that the variable is guarded by that lock” (JCP 28) Locks: Documentation “Every shared, mutable variable should be guarded by exactly one lock. Make it clear to maintainers which lock that is” - (JCP p28) Locks: Across Invariants “For every invariant that involves more that one variable, all the variables involved in that invariant must be guarded by the same lock” - (JCP p29) Critical Concept Any mutable state that can be concurrently accessed, must be synchronized EVERYWHERE else in the program! EXAMPLE: TimerTask (JCP p29) Why Not Lock Everything? Even if every method were synchronized, it doesn’t solve actions that combine those methods! (example: vector) Also, possible liveness and/or performance problems Poor concurrency example (JCP p30) Lock Advice “Avoid holding locks during lengthy computations or operations at risk of not completing quickly such as network or console I/O” - (JCP p32) More Syntax Other use of synchronized public somemethod() { synchronized(this) { // do stuff } } Outline Background Basic Thread Safety Concurrent Object Management Concurrent Library Components Task Execution and Shutdown Sharing Objects Memory Visibility Shared object modification should be visible to other threads Without synchronization, this may not happen More about Visibility “In general, there is no guarantee that the reading thread will see a value written by another thread on a timely basis, or even at all” - (JCP p33) Public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield() System.out.println(number) } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } } NoVisibility :( NoVisibility could loop forever! NoVisibility could write 0 through reordering Other badness and sadness Don’t do this! Synchronization Helps NoVisibility demonstrates stale data Synchronization must be used on all shared variables, even just for reads Enforces memory visibility A Word about 64bit When a thread reads a var w/o synchronization, at worst it is stale, but not random Out-of-thin-air safety One exception: 64 bit numbers (double and long) Read/write can be 2 32 bit operations! Volatile Variables Can declare a java var volatile Volatile ensures visibility, but not locking. Volatile use can be fragile. Many times, you shouldn’t use it Good uses of volatile Ensure visibility of their own state That of the object they refer to Or indicate that an important event has occurred (e.g. shutdown) Use Volatile ONLY when Writes to the var do not depend on its current value or are only written by one thread The var does not participate with invariants with other state vars Locking is not required for any other reason when the var is being accessed Publication and Escape Publishing an object - makes it available outside current scope Many times, objects should not be published at all Breaks encapsulation Not fully constructed (!) Object published when it should not have been is said to have escaped! Publication Methods Public static fields Chained publication Special Case: published inner class Passing to an alien method How Escape Happens All linked to poor design Semantic escapes: Return ref instead of copy Inadvertent chained publication Syntactic escapes: Escaped this during construction “Object not properly constructed” This Escape Example Public class ThisEscape { // JCP p41 public ThisEscape(Eventsource source) { source.registerListener( new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } } Preventing Escape Thread Confinement Immutability Safe Publication Thread Confinement Keep mutable vars confined to a single thread Ad-hoc = enforced by impl. Stack Confinement Local vars Violated by publication ThreadLocal Per thread value holding object Immutable Objects Always thread safe Can’t escape after construction Definition It’s state cannot be modified after construction All fields are final* Properly constructed Can have mutable variables Final Fields Can’t be modified AND, have special semantics in the Java Memory model (initialization safety) Make all fields final by default “Mostly Immutable” is better than mutable Using Volatile Again! Volatile can be used to publish immutable objects Still not locked, but can be safe depending on semantics Example - JCP p49-50 Improper Publication If synchronization is not used to publish a mutable object, the object is not properly published Without proper publication, there are serious problems with stale data Safe Publication Object reference and object state must become visible at the same time! Idioms: Initializing from static initializer Storing ref in volatile or AtomicReference Storing ref in final field of properly constructed object Storing ref in a lock-guarded field Good News! Java Collections safe publication examples: Key or value in Hashtable, synchronizedMap, or ConcurrentMap Insert in Vector, CopyOnWriteArrayList, CopyOnWriteArraySet, synchronizedList, or synchronizedSet Insert in BlockingQueue or ConcurrentLinkedQueue More Good News Effectively Immutable Objects Objects that are used as if they were immutable Example: Date Modification Vs Visibility Safe publication ensures visibility Synchronization is required if the object can be modified post-publication Rules of Engagement Thread-Confined: no sharing Shared read-only: (effectively) immutable objects Shared thread-safe: internal synchronization Guarded: only access with the associated guard Designing Thread-Safe Classes Identify the variables that form the object’s state Identify the invariants that contain the state variables Establish a policy for managing concurrent access to the object’s state Java Monitor Pattern Principle of instance confinement Encapsulate all mutable state and guard it with intrinsic lock Good coarse-grained locking Delegating Thread Safety Safety can often be delgated to an internally-used threadsafe object Collections are especially useful Only works for 1 state var, or multiple independent vars Extending Thread-safe Classes BE CAREFUL! Extending means that the synchronization policy is distributed over multiple separately maintained classes! Inheritance requires the base class to publish enough state Wrappers require that the same lock is used Composition is often less fragile Extension Examples: Inheritance (JCP p72) Wrapping (JCP p72-73) Composition (JCP p74) Outline Background Basic Thread Safety Concurrent Object Management Concurrent Library Components Task Execution and Shutdown Synchronized Collections Are thread safe, but Composite actions require additional locking for semantic correctness Careless use of locking leads to poor performance (think iteration) Iterators The synchronized collections provide iterators These iterators are fail-fast Throws ConcurrentModificationException Concurrent Collections Designed for concurrent access Improved performance with a few minor trade-offs: Your assignment: go look up the docs on ConcurrentHashMap and CopyOnWriteArrayList and try them out! Blocking Queues Support the producerconsumer pattern Can be bounded or unbounded Serial thread confinement Try it out! Blocking Threads may block Wait Wait Wait Wait for for for for I/O Lock Thread.sleep computation put and get from BlockingQueue block Interruption interrupt() method Request a blocking thread stop A blocking method should throw an InterruptedException If you catch this Propagate the exception OR Restore the interrupt Example: Try{ someBlockingMethod(); } catch (InterruptedException) { Thread.currentThread().interrupt(); } Synchronizers Latches: one-time trigger e.g. CountDownLatch FutureTask: long-running result Has a get() method Returns value if done Blocks until it is done if not More Synchronizers Semaphore: virtual permits Now a library class! Barriers: wait for enough threads Useful for threaded stepping Exchanger 2-party barrier Outline Background Basic Thread Safety Concurrent Object Management Concurrent Library Components Task Execution and Shutdown Advanced Execution The Executor Framework Decouples task submission and task execution Simple interface: void execute(Runnable command) Easiest way to implement a producer-consumer design in an application Execution Policies Answers In what thread will tasks be run What order should they be run How many concurrently? How many queued? “Victim” selection Setup and Teardown Policy Examples Single-thread (JCP p119) 1-thread-per-client (JCP p118) Thread Pools: some built in newFixedThreadPool newCachedThreadPool newSingleThreadExecutor newScheduleThreadPool Cancellation & Shutdown Reasons for cancellation User-requested Time-limited Events Errors Shutdown Cancellation Policy “how” “when” and “what” of cancellation Not enforced by Java, but generally, use interruption for cancellation E.g. have a cancel() method call the interrupt Interruption Policy Determines how a thread interprets an interruption request Tasks are “guests” in a thread Preserve interruption status Only call interrupt, when you know the interruption policy Detecting Interrupts Polling the thread’s interrupted status Detecting an InterruptedException Non-interruptible Blocks Synchronous socket I/O: Close the socket Asynchronous I/O with Selector: Call wakeup Lock Acquisition: Use the explicit Lock class Has a lockInterruptibly() method Stopping a ThreadBased Service ExceutorService extends Executor Provides “Lifecycle” options shutdown() awaitTermination(timeout, unit) Any thread based service should provide similar methods Waiting for Threads In the “raw” case, use join() A number of Java library classes have advanced “waits” like ExecutionService’s awaitTermination() Obviously, if you implement your own ExecutionService, you’ll have to use the raw stuff