cse 219 computer science iii multithreaded issues

Post on 30-Dec-2015

221 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

CSE 219Computer 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

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

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

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

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

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

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

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

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

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…

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

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

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

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

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

}

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);}

}}

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

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

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

Using Object Locks

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();

}

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

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

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

Dining Philosophers Problem

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

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

top related