Download presentation
Presentation is loading. Please wait.
Published byFaith Hicklin Modified over 9 years ago
1
Concurrency 101 Shared state
2
Part 1: General Concepts 2
3
Best book Get this book if you ever need to do concurrent programming in Java! Of course, everything in Java is also available in Scala 3
4
Definitions Parallel processes: Happening simultaneously (hardware term) Concurrent processes: Happening asynchronously Asynchronous: Having no defined ordering of events Thread-safe: Correct when accessed from multiple threads Stateless: Containing no mutable quantities Atomic: Happens "all at once" or not at all Race condition: Result depends on order of access Locking: Preventing other threads from accessing a piece of data Reentrant lock: Can access if already have the lock Deadlock: Neither thread can progress until other one does Livelock: Threads fail to get out of each others way Starvation: A thread never gets a chance to run 4
5
Locking Locking, done correctly, gives a thread temporary, exclusive access to a piece of data A thread requests a lock, or monitor, and blocks (does not proceed) until it gets it When it gets the lock, it does its work, and releases the lock In Java, any object can serve as a lock Another thread that requests the same lock may have to wait for it Extremely important: There is no necessary connection between the lock and the data that the thread wants to manipulate 5
6
When to synchronize In Java, locking goes by the name synchronization When two or more threads have access to the same mutable state, every access, everywhere, must be synchronized. No exceptions for any reason! When an invariant involves more than one variable, all such variables must be protected with the same lock Synchronization can be extremely expensive Improper synchronization can lead to race conditions, deadlock, livelock, starvation, or failed visibility Immutable values never need to be synchronized 6
7
Visibility Proper synchronization ensures that threads always see the most up-to-date values of mutable variables Otherwise: Variables may not get updated The compiler may reorder statements that access variables Variables may remain in the cache Declaring a variable to be volatile tells the compiler not to cache the variable or reorder statements This guarantees visibility, but not atomicity 32-bit variables may be stale (hold old values), but they will be legal values This guarantee does not hold for 64-bit values ( double and long ) 7
8
Other safety measures Never start a thread from inside a constructor Volatile variables, if written only from a single thread, may be read from multiple threads without synchronization Some Java-provided data structures are thread-safe; others are not. Pay attention to which is which. Even thread-safe data structures can be used in an unsafe way Local variables are thread-safe; they cannot be shared ThreadLocal variables are those for which each thread has a separate copy 8
9
Object safety You must understand an object's invariants (what makes it internally consistent) and postconditions (how it is allowed to change) Proper synchronization is easier to guarantee if all access is via the object's methods 9
10
Summary (Goetz, page 110) It’s the mutable state, stupid. Make fields final unless they need to be mutable. Immutable objects are automatically thread-safe. Encapsulation makes it practical to manage the complexity. Guard each mutable variable with a lock. Guard all variables in an invariant with the same lock. Hold locks for the duration of compound actions. A program that accesses a mutable variable from multiple threads without synchronization is a broken program. Don’t rely on clever reasoning about why you don’t need to synchronize. Include thread safety in the design process - or explicitly document that your class is not thread-safe. Document your synchronization policy. 10
11
Part 2: Tools 11
12
12 Creating and starting Threads There are two ways to create a Thread: Define a class that extends Thread Supply a public void run() method Create an object o of that class Tell the object to start: o.start(); Define a class that implements Runnable (hence it is free to extend some other class) Supply a public void run() method Create an object o of that class Create a Thread that “knows” o : Thread t = new Thread(o); Tell the Thread to start: t.start(); 12
13
Synchronizing Threads in Java Java has a synchronized statement: synchronized (lock) { code } In Java, any object can serve as a lock (monitor) Java has synchronized instance methods: synchronized public void myMethod(args) { body } This is equivalent to public void myMethod(args) { synchronized(this) { statements } } Java has synchronized static methods: public static void myMethod(args) { statements } They are synchronized on the class object (a built-in object that represents the class) 13
14
14 Synchronizing in Scala Same concepts, slightly different syntax A synchronized code block: myObject.synchronized { // code block } A synchronized method: def myMethod(args) = synchronized { // code block }
15
Thread pools Since threads are expensive to create, Java provides a way to recycle threads import java.util.concurrent.Executors; ExecutorService pool = Executors.newFixedThreadPool(poolSize); Create some Runnable objects (objects that implement public void run() exec.execute(Runnable object) void shutdown () stops accepting new tasks List shutdownNow() stops all tasks and returns a list of unfinished tasks 15
16
Some thread-safe data structures In java.util : Hashtable (not HashMap ) Vector In java.util.concurrent : ConcurrentHashMap ArrayBlockingQueue ConcurrentLinkedQueue FutureTask 16
17
17 Check-then-act A Vector is like an ArrayList, but is synchronized (so, thread safe) Hence, the following code looks reasonable: if (!myVector.contains(someObject)) { // check myVector.add(someObject); // act } But there is a “gap” between checking the Vector and adding to it During this gap, some other Thread may have added the object to the array Check-then-act code, as in this example, is unsafe You must ensure that no other Thread executes during the gap synchronized(myVector) { if (!myVector.contains(someObject)) { myVector.add(someObject); } } So, what good is it that Vector is synchronized? It means that each call to a Vector operation is atomic 17
18
java.util.concurrent.atomic The java.util.concurrent.atomic package provides classes and methods to help avoid the check-then-act problem For example, here are some methods in AtomicInteger : int addAndGet(int delta) Atomically adds the given value to the current value. boolean compareAndSet(int expect, int update) Atomically sets the value to the given updated value if the current value == the expected value. int decrementAndGet() Atomically decrements by one the current value. int getAndAdd(int delta) Atomically adds the given value to the current value. boolean weakCompareAndSet(int expect, int update) Atomically sets the value to the given updated value if the current value == the expected value. 18
19
The End 19
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.