2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo...

37
2006-08-08 Java Threads 1 Threading and Concurrent Programming in Java Synchronization D.W. Denbo

description

Java Threads3 Race Condition In most real applications, two or more threads will need to share access to the same objects. What happens if two threads have access to the same object calls a methods that changes the object state? As you might expect the threads can step on each other’s toes. Such a situation is often called a race condition.

Transcript of 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo...

Page 1: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 1

Threading and Concurrent

Programming in JavaSynchronization

D.W. Denbo

Page 2: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 2

Outline• Race condition• Lock objects• synchronized keyword• Synchronized block• Volatile Fields• Deadlocks• Lock testing

Page 3: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 3

Race ConditionIn most real applications, two or more threads will need to share access to the same objects. What happens if two threads have access to the same object calls a methods that changes the object state? As you might expect the threads can step on each other’s toes. Such a situation is often called a race condition.

Page 4: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 4

Example of a Race Condition

To avoid corruption of share data, you must learn how to synchronize the access.

In this example, we have the class Bank with the method transfer. Money can be transferred between accounts. A thread is created for each account to transfer money from its account to another.

Page 5: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 5

public void transfer(int from, int to, double amount) {System.out.print(Thread.currentThread());accounts[from] -= amount;System.out.printf(“%10.2f from %d to $d”,amount,from,to);accounts[to]+= amount;System.out.printf(“Total: %10.2f%n”,getTotalBalance());

}

Page 6: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 6

public void run() {try {

int toAccount = (int)(bank.size()*Math.random());double amount = maxAmount*Math.random();bank.transfer(fromAccount, toAccount, amount);Thread.sleep((int)(DELAY*Math.random()));

} catch (InterruptedException ie) {}

}

Page 7: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 7

UnsynchBankTest

Page 8: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 8

While this simulation is running, we don’t know how much money is in any one account. We do, however, know that the total amount of money should remain constant!

Page 9: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 9

Errors have crept in and some amount of money was either lost or spontaneously created. Why?

accounts[to] += amount;

The problem is that these are not atomic operations!

What Happened?

Page 10: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 10

The instructions might be processed as follows:

1.Load accounts[to] into a register2.Add amount3.Move the result back to accounts[to].

If the first thread executes Steps 1 & 2, then is interrupted by a second thread that updates the same entry, the second threads changes are lost.

Page 11: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

Thread 1 Thread 2 accounts[to]

5000

Thread 1 register Thread 2 register

5000

5000

5000

5000

5900

5500

5500

5000

5900

5900

5500

load

add

store

load

add

store

Page 12: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 12

Lock ObjectsStarting with JDK 5.0, there are two methods for protecting a code block from concurrent access.

1.The synchronized keyword2.The ReentrantLock class

We’ll first take a closer look at the ReentrantLock:

Page 13: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 13

This construct guarantees that only one thread can access the critical section at a time.

myLock.lock(); // a ReentrantLock objecttry {

// critical section} finally {

myLock.unlock(); // make sure lock is unlocked}

Page 14: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 14

public class Bank {public void transfer(int from, int to, double amount) {bankLock.lock();try {if(accounts[from] < amount) return;System.out.print(Thread.currentThread());accounts[from] -= amount;System.out.printf(“%10.2f from %d to %d”,amount,from,to);accounts[to] += amount;System.out.printf(“Total: %10.2f%n”,getTotalBalance());

} finally {bankLock.unlock();

}}....// Reentrant lock implements the Lock interfaceprivate Lock bankLock = new ReentrantLock();

}

Page 15: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 15

• Note that• Each Bank object has its own ReentrankLock

• If two threads try to access the same Bank object, then the lock will serialize access.

• If two threads try to access different Bank objects, each thread acquires a different lock and neither thread is blocked.

Page 16: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 16

Condition ObjectsOften, a thread enters a critical section, only to discover that it can’t proceed until a condition is fulfilled. You use a condition object to manage threads that have acquired lock but cannot do useful work. Lets extend our example to cover the case of insufficient funds.

if(bank.getBalance(from) >= amount)bank.transfer(from, to, amount);

The above won’t work!

Page 17: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 17

public void transfer(int from, int to, double amount) {bankLock.lock();try {

while(accounts[from] < amount) {// wait

}// transfer funds

} finally {bankLock.unlock();

}}

So what do we do when there isn’t enough money? We wait until some other thread puts enough money in!

Page 18: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 18

A lock object can have one or more associated condition objects. Here we set up a condition object to represent the “sufficient funds” condition.

class Bank {public Bank() {

....sufficientFunds = bankLock.newCondition();

}....private Condition sufficientFunds;

}

Page 19: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 19

public void transfer(int from, int to, double amount) {bankLock.lock();try {

while(accounts[from] < amount) sufficientFunds.await();

// transfer funds...sufficientFunds.signalAll();

} finally {bankLock.unlock();

}}

NOTE: the call to signalAll does not immediately activate a waiting thread. It only unblocks the waiting threads so that they can compete for entry into the object.

Page 20: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 20

SynchBankTest

Page 21: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 21

• A lock protects sections of code, allowing only one thread access at a time.

• A lock manages threads that are trying to enter protected code segments.

• A lock can have one more associated condition objects.

• Each condition object manages threads that have entered a protected code section but cannot proceed.

Page 22: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 22

synchronized KeywordEvery object in Java has an implicit lock. If a method is declared with the synchronized keyword, then the object’s lock protects the entire method. This implicit lock has a single implicit condition. The wait method adds a thread to the wait set, and the notifyAll/notify methods unblock the waiting threads.

Page 23: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 23

class Bank {public synchronized void transfer(int from, int to, double amount) {

while(accounts[from] < amount) wait(); // wait on objects lock’s single condition

accounts[from] -= amount;accounts[to] += amount;notifyAll(); // notify all threads waiting on the condition

}public synchronized double getTotalBalance() {....}private double accounts[];

}

Page 24: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 24

• While using synchronized yields more concise code, implicit locks and conditions have some limitations.• Cannot interrupt a thread that is trying to acquire a lock

• Cannot specify a timeout when trying to acquire a lock

• Having a single condition per lock can be inefficient

• The virtual machine locking primitives do not map well to the most efficient locking mechanisms available in hardware.

Page 25: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 25

• Core Java authors recommendations• Best to use neither Lock/Condition or the synchronized keyword. Should first see if java.util.concurrent package offers a solution.

• If the synchronized keyword works for your solution, you should use it. You write less code and have less room for error.

• Use Lock/Condition if you specifically need the additional power that these constructs give you.

Page 26: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 26

Synchronized BlocksIf you deal with legacy code (or legacy programmers :-), you need to know something about the built-in synchronization primitives. Recall each object has a lock.

class Bank {public void transfer(int from, int to, double amount) {

synchronized(lock) { // an ad-hoc lockaccounts[from] -= amount;accounts[to] += amount;

}}private double accounts[];private Object lock = new Object();

}

Page 27: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 27

Volatile Fields• Computers with multiple processors can temporarily hold memory values in registers or local memory caches. Threads running in different processors may see different values for the same memory location!

• Compilers can reorder instructions for maximum throughput. Compilers won’t change the meaning of the code, but will assume that all changes occur within the block of optimized code. However, a memory value can be changed by another thread!

Page 28: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 28

• Concurrent access to a field is safe in these three conditions:• The field is volatile• The field is final, and it is accessed after the constructor has completed.

• The field access is protected by a lock

• NOTE: Prior to JDK 5.0, the semantics of volatile were rather permissive.

Page 29: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 29

Deadlocks• Locks and conditions cannot solve all problems that might arise in multithreading. Consider ...• Account 1: $200• Account 2: $300• Thread 1: Transfer $300 from Account 1 to 2• Thread 2: Transfer $400 from Account 2 to 1.

• In the example, deadlocks can’t occur because each transfer is limited to $1000. With 100 acounts and $100,000 total, at least on account must have $1000. What if we change this restriction?

Page 30: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 30

SynchBankTest_dl

Page 31: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 31

Creating Deadlocks• You can create a deadlock by making the i’th thread responsible for putting money into the i’th account, rather than for taking it out of the i’th account. It becomes possible for all the threads to gang up on one account, each trying to remove more money that it contains.

• Another deadlock situation: Change the signalAll method to signal in the SynchBankTest program. It deadlocks because it is now only unblocking a single thread, if that thread can’t proceed...

• Unfortunately, there is nothing in the Java language to avoid or break these deadlocks.

Page 32: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 32

Lock Testing and Timeouts

• A thread blocks indefinitely when it calls the lock method to acquire a lock that is owned by another thread. Other things you can try:• tryLock boolean method. Returns true if lock acquired.

• tryLock(100, TimeUnit.MILLISECONDS), with a timeout. tryLock can be unfair and grab the lock without waiting its turn, with the timeout tryLock is fair.

• myCondition.await(1000, TimeUnit.MICROSECONDS), will return if notifyAll has been called by another thread, or if the timeout has elapsed.

Page 33: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 33

Read/Write Locks• java.util.concurrent.locks package defines two lock classes, ReentrantLock and ReentrantReadWriteLock. The latter is advantageous if many threads read and only a few write.

Page 34: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 34

private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();private Lock readLock = rwl.readLock();private Lock writeLock = rwl.writeLock();

public double getTotalBalance() {readLock.lock();try { .... } finally {

readLock.unlock();}

}

public void transfer(...) {writeLock.lock();try { ...} finally {

writeLock.unlock();}

}

Page 35: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 35

Why the stop and suspend Methods are

Deprecated• JDK 1.0 defined a stop method that simply terminates a thread, and a suspend method that blocks a thread.

• Both of these methods have been deprecated since JDK 1.2. The stop method is inherently unsafe, and experience has shown that the suspend method can lead to deadlocks.

Page 36: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 36

stop methodThis method terminates all pending methods, including the run method. When a thread is stopped, it immediately gives up the locks on all objects that it has locked. This can leave objects in an inconsistent state. For example, suppose a TransferThread is stopped in the middle of moving money from one account to another, after the withdrawal and before the deposit? Now the Bank object is damaged.

Page 37: 2006-08-08 Java Threads 11 Threading and Concurrent Programming in Java Synchronization D.W. Denbo Synchronization D.W. Denbo.

2006-08-08 Java Threads 37

suspend MethodUnlike stop, suspend won’t damage objects. However, if you suspend a thread that owns a lock, then the lock is unavailable until the thread is resumed. If the thread that calls the suspend method tries to acquire the same lock, the program deadlocks. This situation occurs frequently in GUIs.

If you want to suspend a thread, introduce a variable and test it in a safe place of your run method.