Concurrency http://csunplugged.org/routing-and-deadlock CSCI 136: Fundamentals of Computer Science II Keith Vertanen
Overview Multi-threaded programs Multiple simultaneous paths of execution Seemingly at once (single core) Actually at the same time (multiple cores) Concurrency issues The dark side of threading Unpredictability of thread scheduler can get you Protecting shared data: synchronized methods Deadlock The really dark side of threading 2
Review: creating & starting a thread public class BlastOff implements Runnable public void run() for (int i = 10; i > 0; i--) System.out.print(i + " "); System.out.println("BLAST OFF!"); public class Launch public static void main(string [] args) System.out.println("prepare for launch"); Thread thread = new Thread(new BlastOff()); thread.start(); System.out.println("done with launch"); % java Launch prepare for launch done with launch 10 9 8 7 6 5 4 3 2 1 BLAST OFF! % java Launch prepare for launch 10 9 done with launch 8 7 6 5 4 3 2 1 BLAST OFF! % java Launch prepare for launch 10 done with launch 9 8 7 6 5 4 3 2 1 BLAST OFF! 3
Review: Multithreading for speed Goal: Count how often different integers occur In a large array of integers Randomly generated in [0, 100) Have one thread handle each target integer % java ParallelSearch 1000000 2 7 16 42 99 Starting workers... Count of 2 = 9888 Count of 7 = 10222 Count of 16 = 9989 Count of 42 = 10099 Count of 99 = 9894 4
public class SearchWorker implements Runnable private int target = 0; private int [] data = null; private int result = 0; public SearchWorker(int target, int [] data) this.target = target; this.data = data; public int getresult() return result; public int gettarget() return target; public void run() for (int i = 0; i < data.length; i++) if (data[i] == target) result++; Worker object: One of these is created for each target integer we want to search for. Needs to keep track of its input: what number to search for, the array to search in. Must remember its output: count of the target in the array. 5
... final int DATA_SIZE = Integer.parseInt(args[0]); final int WORKERS = args.length - 1; int [] data = new int[data_size]; for (int i = 0; i < DATA_SIZE; i++) data[i] = (int) (Math.random() * 100); SearchWorker [] workers = new SearchWorker[WORKERS]; Thread [] threads = new Thread[WORKERS]; for (int i = 0; i < WORKERS; i++) workers[i] = new SearchWorker(Integer.parseInt(args[i + 1]), data); threads[i] = new Thread(workers[i]); threads[i].start(); for (int i = 0; i < WORKERS; i++) try threads[i].join(); catch (InterruptedException e) e.printstacktrace(); System.out.printf("Count of %d = %d\n",... Client program: 1. Parses command line arguments. 2. Creates random array of data to search in. 3. Creates each worker, launches each worker in its own thread. 4. Waits for each thread to finish, printing out the worker s result. workers[i].gettarget(), workers[i].getresult()); 6
Trouble in Concurrency City: Act 1 Lost update problem Multiple threads All sharing a single counter object Each thread increments fixed number of times public class Count private int count = 0; public int getcount() return count; public class IncrementWorker implements Runnable private Count count = null; public IncrementWorker(Count count) this.count = count; public void increment() count++; public void run() for (int i = 0; i < 1000; i++) count.increment(); 7
Lost update problem... Count count = new Count(); Thread [] threads = new Thread[N]; for (int i = 0; i < N; i++) threads[i] = new Thread(new IncrementWorker(count)); threads[i].start(); for (int i = 0; i < N; i++) try threads[i].join(); catch (InterruptedException e) e.printstacktrace(); System.out.println("Final count = " + count.getcount());... % java Increment 1 Final count = 1000 % java Increment 2 Final count = 2000 % java Increment 20 Final count = 20000 % java Increment 20 Final count = 19761 % java Increment 20 Final count = 19014 8
Synchronizing methods Only allow 1 worker in increment at a time! Tell Java this using synchronized keyword public class Count private int count = 0; public int getcount() return count; public synchronized void increment() count++; % java Increment 20 Final count = 20000 % java Increment 20 Final count = 20000 % java Increment 20 Final count = 20000 9
Locking an object Each object instance has a lock Multiple methods can be marked synchronized Only one can be executing at any time Locking is on the object level On the instance of the class, not the class data type itself Locking and unlocking takes time Only synchronize methods if necessary public void run() for (int i = 0; i < 1000; i++) synchronized (count) count.increment(); Another approach to fixing the lost update problem. Synchronization can be enforced on a block of code instead of an entire method. 10
Trouble in Concurrency City: Act 2 Concurrent access to same data structure Many built-in containers are not thread-safe! e.g. ArrayList, HashMap Program will crash (probably) Protect all reading/writing to shared structure Via synchronized method or synchronized code block 11
public class ArrayListBad implements Runnable private static ArrayList<Integer> list = new ArrayList<Integer>(); public void run() for (int i = 0; i < 10; i++) list.add((int) (Math.random() * 100)); System.out.println("Thread all done..."); public static void main(string[] args) Thread t1 = new Thread(new ArrayListBad()); t1.start(); Thread t2 = new Thread(new ArrayListBad()); t2.start(); try t1.join(); t2.join(); catch (InterruptedException e) e.printstacktrace(); System.out.println("Phew, we made it!"); May sometimes work, especially if loop in run() is short. But most likely you ll get some sort of exception: Exception in thread "Thread-1" Thread all done... java.lang.arrayindexoutofboundsexception: 15 at java.util.arraylist.add(unknown Source) at ArrayListBad.run(ArrayListBad.java:12) at java.lang.thread.run(unknown Source) Phew, we made it! 12
public class ArrayListGood implements Runnable private static ArrayList<Integer> list = new ArrayList<Integer>(); public void run() for (int i = 0; i < 100000; i++) synchronized(list) list.add((int) (Math.random() * 100)); System.out.println("Thread all done...");... Adding synchronized block protects the shared static list instance variable, now only one thread is allowed to add at any point in time. 13
Trouble in Concurrency City: Act 3 Deadlock Program stops doing anything useful All you need is 2 objects and 2 threads 14
Programming activity Goal: Increment/decrement all ints in an array Create class NumHolder, holds array 100 integers Create increment() and decrement() methods Methods go through all 100 integers and ++ or -- them Create run() method Loop 1000 times, each loop flip coin and call either increment() or decrement() Create main program in NumHolderLaunch Create a single NumHolder object Create two threads Print out NumHolder object Start threads, wait for them to finish Print out NumHolder again 15