concurrency 101 shared state. part 1: general concepts 2

19
Concurrency 101 Shared state

Upload: faith-hicklin

Post on 16-Dec-2015

213 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Concurrency 101 Shared state. Part 1: General Concepts 2

Concurrency 101

Shared state

Page 2: Concurrency 101 Shared state. Part 1: General Concepts 2

Part 1: General Concepts

2

Page 3: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 4: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 5: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 6: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 7: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 8: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 9: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 10: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 11: Concurrency 101 Shared state. Part 1: General Concepts 2

Part 2: Tools

11

Page 12: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 13: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 14: Concurrency 101 Shared state. Part 1: General Concepts 2

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}

Page 15: Concurrency 101 Shared state. Part 1: General Concepts 2

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<Runnable> shutdownNow() stops all tasks and

returns a list of unfinished tasks

15

Page 16: Concurrency 101 Shared state. Part 1: General Concepts 2

Some thread-safe data structures

In java.util: Hashtable (not HashMap) Vector

In java.util.concurrent: ConcurrentHashMap ArrayBlockingQueue ConcurrentLinkedQueue FutureTask

16

Page 17: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 18: Concurrency 101 Shared state. Part 1: General Concepts 2

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

Page 19: Concurrency 101 Shared state. Part 1: General Concepts 2

The End

19