Concurrency
Fundamentals of Computer Science
http://csunplugged.org/routing-and-deadlock
Concurrency http://csunplugged.org/routing-and-deadlock - - PowerPoint PPT Presentation
Concurrency http://csunplugged.org/routing-and-deadlock Fundamentals of Computer Science Outline Multi-threaded programs Multiple simultaneous paths of execution Seemingly at once (single core) Actually at the same time (multiple
Fundamentals of Computer Science
http://csunplugged.org/routing-and-deadlock
Multiple simultaneous paths of execution Seemingly at once (single core) Actually at the same time (multiple cores)
The dark side of threading Unpredictability of thread scheduler Protecting shared data: synchronized methods
The really dark side of threading
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!
% 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
In a large array of integers Randomly generated in [0, 100) Have one thread handle each target integer
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++; } } }
... 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", workers[i].getTarget(), workers[i].getResult()); } ...
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 void increment() { count++; } } public class IncrementWorker implements Runnable { private Count count = null; public IncrementWorker(Count count) { this.count = count; } public void run() { for (int i = 0; i < 1000; i++) count.increment(); } }
... 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
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
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(); } } }
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
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!"); } }
12
run() is short.
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!
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..."); } ...
13
Program stops doing anything useful All you need is 2 objects and 2 threads
Multiple simultaneous paths of execution Seemingly at once (single core) Actually at the same time (multiple cores)
The dark side of threading Unpredictability of thread scheduler Protecting shared data: synchronized methods
The really dark side of threading
Goal: Increment/decrement all ints in an array
Create class NumHolder, holds array of 100 ints
Create increment() and decrement() methods
Methods that 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, passing them the NumHolder object you created Print out NumHolder object Start threads, wait for them to finish Print out NumHolder again
Hint: All numbers should be the same in the second print of NumHolder
Open Moodle, go to CSCI 136, Section 11 Open the dropbox In-Class Exercise 8 Drag and drop your program file to the Moodle dropbox You get: 1 point if you turn in something, 2 points if you turn in