concurrency 101 shared state. part 1: general concepts 2

Post on 16-Dec-2015

213 Views

Category:

Documents

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

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

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

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

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

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

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

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

Part 2: Tools

11

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

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

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}

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

Some thread-safe data structures

In java.util: Hashtable (not HashMap) Vector

In java.util.concurrent: ConcurrentHashMap ArrayBlockingQueue ConcurrentLinkedQueue FutureTask

16

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

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

The End

19

top related