CSE 219 Computer Science III Multithreaded Issues.

26
CSE 219 Computer Science III Multithreaded Issues

Transcript of CSE 219 Computer Science III Multithreaded Issues.

Page 1: CSE 219 Computer Science III Multithreaded Issues.

CSE 219Computer Science III

Multithreaded Issues

Page 2: CSE 219 Computer Science III Multithreaded Issues.

Java Thread Issues• We’ve used threads naively

• Many things to consider when using multithreading in an application:– Swing & threads– race conditions– locking threads– deadlock

Page 3: CSE 219 Computer Science III Multithreaded Issues.

Swing & Threads• NOTE: Swing is not thread-safe

• We have to be careful when mixing GUIs (Swing) and Concurrency (Threads)

• The screen should never “freeze”

• The program should always be responsive to user interaction– no matter what it may currently be doing

Page 4: CSE 219 Computer Science III Multithreaded Issues.

Race Conditions• Threads are independent pieces of control operating on

the same data.• One thread could interfere with another thread’s work

– results in corrupted data

– called a race condition

• Common issue in Consumer – Producer relationships– one thread is the producer, adding to a shared data structure

– one thread is the consumer, using a shared data structure

• 2003 Blackout problem? Race Conditions in software:– http://www.securityfocus.com/news/8412

• When do race conditions happen?– when transactions lack atomicity

Page 5: CSE 219 Computer Science III Multithreaded Issues.

Atomicity• A property of a transaction

• An atomic transaction happens either completely or not at all

• What’s a transaction?– code execution (ex: method) that changes stored data

• Ex: instance variables, static variables, database

Page 6: CSE 219 Computer Science III Multithreaded Issues.

Example: A Corruptible Bank

BuggedTransferer

-bank:BadBank

-fromAccount:int

+$MAX:double

+$DELAY:int

+run():void

BadBank

-account: double[]

+$INIT_BALANCE:double

+$NUM_ACCOUNTS:int

+transfer(from:int,

to:int,

double:amount):void

-getTotalBalance():double

<<interface>>

Runnable

• Assumptions:– We will create a single BadBank and make random transfers, each in separate

threads

100 1

Page 7: CSE 219 Computer Science III Multithreaded Issues.

public class BadBank { public static int INIT_BALANCE = 1000, NUM_ACCOUNTS = 100; private double[] accounts = new double[NUM_ACCOUNTS];

public BadBank() {for (int i = 0; i < NUM_ACCOUNTS; i++)

accounts[i] = INIT_BALANCE; }

public void transfer(int from, int to, double amount) {if (accounts[from] < amount) return;accounts[from] -= amount;System.out.print(Thread.currentThread());System.out.printf("%10.2f from %d to %d",amount,from,to);accounts[to] += amount;double total = getTotalBalance();System.out.printf(" Total Balance: %10.2f%n", total);

}

private double getTotalBalance() {double sum = 0;for (double a : accounts) sum += a;return sum;

}} BadBank.java

Page 8: CSE 219 Computer Science III Multithreaded Issues.

public class BuggedTransferer implements Runnable {private BadBank bank;private int fromAccount;public static final double MAX = 1000;public static final int DELAY = 100; public BuggedTransferer(BadBank b, int from) { bank = b; fromAccount = from;} public void run() { try { while(true) { int toAccount = (int)(bank.NUM_ACCOUNTS * Math.random()); double amount = MAX * Math.random(); bank.transfer(fromAccount, toAccount, amount); Thread.sleep((int)(DELAY*Math.random()));

} } catch(InterruptedException e) {/*SQUELCH*/}

}}

BuggedTransferer.java

Page 9: CSE 219 Computer Science III Multithreaded Issues.

public class AtomiclessDriver {

public static void main(String[] args) {

BadBank b = new BadBank();

for (int i = 0; i < BadBank.NUM_ACCOUNTS; i++) {

BuggedTransferer bT = new BuggedTransferer(b,i);

Thread t = new Thread(bT);

t.start();

}

}

}

AtomiclessDriver.java

Page 10: CSE 219 Computer Science III Multithreaded Issues.

What results might we get?…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 100000.00…Total Balance: 99431.55…Total Balance: 99367.34…

• Why might we get invalid balance totals?• race conditions• operations on shared

data may lack atomicity

• Bottom line:• a method or even a single statement is not an atomic

operation

• this means that the statement can be interrupted during its operation

Page 11: CSE 219 Computer Science III Multithreaded Issues.

A single statement?• Compiled into multiple low-level statements• To see:javap –c –v BadBank• Ex: accounts[from] -= amount;… 21 aload_0 22 getfield #3 <Field double accounts[]> 25 iload_1 26 dup2 27 daload 28 dload_3 29 dsub 30 dastore…

Page 12: CSE 219 Computer Science III Multithreaded Issues.

Race Condition Example• Thread 1 & 2 are in transfer at the same time.

Thread 1 Thread 2aload_0getfield #3iload_1dup2daloaddload_3

aload_0getfield #3iload_1dup2daloaddload_3dsubdastore

dsubdastore

What’s the problem?

This might store corrupted data

Page 13: CSE 219 Computer Science III Multithreaded Issues.

How do we guarantee atomicity?• By locking methods or code blocks• What is a lock?

– a thread gets a lock on critical shared code

– only one thread may execute locked code at a time

• Two techniques:1. Locking code blocks via the ReentrantLock class

• new to Java in 1.5

2. Synchronizing methods or code blocks

• Synchronization is easier to use, ReentrantLock with Condition classes are more powerful

• Can we lock an object?– yes, we’ll see how in a few moments

Page 14: CSE 219 Computer Science III Multithreaded Issues.

ReentrantLock• Basic structure to lock critical code:ReentrantLock myLock = new ReentrantLock();

myLock.lock();

try {

// CRITICAL AREA TO BE ATOMICIZED

}

finally {

myLock.unLock();

}

• When a thread enters this code:– if no other lock exists, it will execute the critical code

– otherwise, it will wait until previous locks are unlocked

Page 15: CSE 219 Computer Science III Multithreaded Issues.

Updated transfer methodpublic class GoodBank { private ReentrantLock bankLock = new ReentrantLock(); private double[] accounts = new double[NUM_ACCOUNTS];… public void transfer(int from, int to, double amount) {

bankLock.lock();try {

if (accounts[from] < amount) return; accounts[from] -= amount; System.out.print(Thread.currentThread()); System.out.printf("%10.2f from %d to%d",amount,from,to); accounts[to] += amount; double total = getTotalBalance(); System.out.printf(" Total Balance: %10.2f%n", total);

} finally{bankLock.unlock();

} }}

• NOTE: This works because transfer is the only mutator method for accounts– What if there were more than one?

• then we’d have to lock the accounts object

Page 16: CSE 219 Computer Science III Multithreaded Issues.

Synchronizing Methodspublic synchronized void deposit(int d) {…}

• A monitor (object with synchronized methods) allows one thread at a time to execute a synchronized method on the object– Locks the method when the synchronized method is invoked

– Only one synchronized method may be active on an object at once

– All other threads attempting to invoke synchronized methods must wait

– When a synchronized method finishes executing, the lock on the object is released and the monitor lets the highest-priority ready thread proceed

• Note: Java also allows synchronized blocks of code. Ex:synchronized (this) {

while (blah blah blah)

blah blah blah

}

Page 17: CSE 219 Computer Science III Multithreaded Issues.

Synchronized transfer methodpublic class SynchronizedBank {… public synchronized void transfer

(int from, int to, double amount) { if (accounts[from] < amount) return; accounts[from] -= amount; System.out.print(currentThread()); System.out.printf("%10.2f from %d to%d",

amount,from,to); accounts[to] += amount; double total = getTotalBalance(); System.out.printf(" Total Balance:

%10.2f%n", total);}

}}

Page 18: CSE 219 Computer Science III Multithreaded Issues.

Locking Data• synchronized is fine for locking methods, but what

about data?– the synchronized keyword cannot be used on variables– Ex: the account variable

• to lock/unlock data we can use a combination of approaches:– use only synchronized methods for manipulating shared dataOR– define a method for acquiring a lock on data– while an object is locked, make all other threads wanting to

use it wait– define a method for releasing a piece (or pieces) of data,

notifying all other threads when doneOR– declare variables as volatile

Page 19: CSE 219 Computer Science III Multithreaded Issues.

public class GoodBank { private double[] accounts = new double[NUM_ACCOUNTS]; private boolean inUse = false;

public synchronized void acquire() throws InterruptedException { while (inUse) wait(); inUse = true; notify(); } public synchronized void release() { inUse = false; notify(); } // ALL METHODS USING accounts MUST GET LOCKS FIRST

Locked accounts object

Page 20: CSE 219 Computer Science III Multithreaded Issues.

// SOMEWHERE INSIDE GoodBank classacquire();… // DO LOTS OPERATIONS THAT… // MAY MANIPULATE THE accounts… // OBJECT AS PART OF SOME… // GREATER ALGORITHMrelease();

Using Object Locks

Page 21: CSE 219 Computer Science III Multithreaded Issues.

wait and notifyAll• wait() blocks the current thread (potentially forever)

– another thread must notify it– all Objects have a wait method that can be called

public synchronized void acquire() throws InterruptedException { while (inUse) wait(); inUse = true; notify(); }

• notify() & notifyAll() unblock a thread or all threads waiting on the current object– must be called at the end of a synchronized method, else you

may get an IllegalMonitorStateExceptionpublic synchronized void release(Object requestor) {

inUse = false;notifyAll();

}

Page 22: CSE 219 Computer Science III Multithreaded Issues.

notify and notifyAll• A thread T1 blocked by wait() is made

runnable when another thread T2 invokes notifyAll() on the object T1 operates on.

• notify() wakes up one of the waiting threads• notifyAll() wakes up all waiting threads

• Once a thread is notified– it attempts to obtain lock again, and if successful it

gets to lock all other threads out

Page 23: CSE 219 Computer Science III Multithreaded Issues.

public class GoodBank { private volatile double[] accounts = new double[NUM_ACCOUNTS];

• The volatile keyword guarantees single threaded use• More overhead for JVM, so slower• Mixed results for use

Alternative: volatile

Page 24: CSE 219 Computer Science III Multithreaded Issues.

What’s the worst kind of race condition?• One that rarely happens, but causes devastating

effects

• Hard to find, even during extensive testing

• Can be hard to simulate, or deliberately produce– depends on thread scheduler, which we don’t control

• Surface only during catastrophes

• Moral of the story, don’t rely on thread scheduler– make sure your program is thread safe

• should be determined logically, before testing

Page 25: CSE 219 Computer Science III Multithreaded Issues.

Dining Philosophers Problem

• 5 philosophers• 5 forks• 5 plates • 1 large bowl of spaghetti in center

Page 26: CSE 219 Computer Science III Multithreaded Issues.

Deadlocks and other matters• Deadlock:

– a thread T1 holds a lock on L1 and wants lock L2

AND– a thread T2 holds a lock on L2 and wants lock L1.– problem: a thread holds a lock on one or more

objects when it invoked wait() on itself

• Morals of the story:– don’t let waiting threads lock other data

• release all their locks before going to wait• there are all sorts of proper algorithms for thread lock

ordering (you’ll see if you take CSE 306)

– a thread cannot be resumed if a wait is not matched by a notify