1 / 44

Chapter 13: Concurrency

Chapter 13: Concurrency. Sometimes we want to execute several parts of a program at the same time This is needed to provide GUI and for client/server programming Each of those parts is named as a thread When we program a thread, we assume that it has its own CPU

eduardom
Télécharger la présentation

Chapter 13: Concurrency

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 13: Concurrency • Sometimes we want to execute several parts of a program at the same time • This is needed to provide GUI and for client/server programming • Each of those parts is named as a thread • When we program a thread, we assume that it has its own CPU • If a computer has only one CPU then its time is shared between threads (time-sliced) • A process is a complete program that can be run concurrently with other programs (in a multi-tasking OS) • A process may have several threads

  2. Notes (cont.) • Thread programming is difficult and needs careful coding. Program is not executed serially. • For such programming we need to know how to synchronize threads and how to have communication between them. • Java provides mechanism to make an object (or some part of an object) be executed as a thread.

  3. Motivation • Providing responsive user interfaces • For example a quit bottom lets user stop execution of a program at any time • Providing services to multiple clinet at the same time • For example a web server can service multiple http requests at the same time • Enhancing throughput • For example CPU time is not wasted when some part of program is doing exhustive IO operation

  4. Defining threads public class SimpleThread extends Thread { private static Test monitor = new Test(); private int countDown = 5; private static int threadCount = 0; public SimpleThread() { super("" + ++threadCount); // Store the thread name start(); } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread();

  5. Defining threads (cont.) monitor.expect(new String[] { "#1: 5", "#2: 5", "#3: 5", "#5: 5", "#1: 4", "#4: 5", "#2: 4", "#3: 4", "#5: 4", "#1: 3", "#4: 4", "#2: 3", "#3: 3", "#5: 3", "#1: 2", "#4: 3", "#2: 2", "#3: 2", "#5: 2", "#1: 1", "#4: 2", "#2: 1", "#3: 1", "#5: 1", "#4: 1" }, Test.IGNORE_ORDER + Test.WAIT); } }

  6. Yielding – letting scheduler to interrupt the thread public class YieldingThread extends Thread { private static Test monitor = new Test(); private int countDown = 5; private static int threadCount = 0; public YieldingThread() { super("" + ++threadCount); start(); } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; yield(); } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new YieldingThread();

  7. Sleeping – ceasing execution for a number of milliseconds public class SleepingThread extends Thread { private static Test monitor = new Test(); private int countDown = 5; public SleepingThread() { super("" + ++threadCount); start(); } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws InterruptedException { for(int i = 0; i < 5; i++) new SleepingThread().join();

  8. Priority public class SimplePriorities extends Thread { private static Test monitor = new Test(); private int countDown = 5; private volatile double d = 0; // No optimization public SimplePriorities(int priority) { setPriority(priority); start(); } public String toString() { return super.toString() + ": " + countDown; } public void run() { while(true) { // An expensive, interruptable operation: for(int i = 1; i < 100000; i++) d = d + (Math.PI + Math.E) / (double)i; System.out.println(this); if(--countDown == 0) return; } } public static void main(String[] args) { new SimplePriorities(Thread.MAX_PRIORITY); for(int i = 0; i < 5; i++) new SimplePriorities(Thread.MIN_PRIORITY);

  9. Priority (cont.) monitor.expect(new String[] { "Thread[Thread-1,10,main]: 5", "Thread[Thread-1,10,main]: 4", "Thread[Thread-1,10,main]: 3", "Thread[Thread-1,10,main]: 2", "Thread[Thread-1,10,main]: 1", "Thread[Thread-2,1,main]: 5", "Thread[Thread-2,1,main]: 4", "Thread[Thread-2,1,main]: 3", "Thread[Thread-2,1,main]: 2", "Thread[Thread-2,1,main]: 1", "Thread[Thread-3,1,main]: 5", "Thread[Thread-4,1,main]: 5", "Thread[Thread-5,1,main]: 5", "Thread[Thread-6,1,main]: 5", "Thread[Thread-3,1,main]: 4", "Thread[Thread-4,1,main]: 4", "Thread[Thread-5,1,main]: 4", "Thread[Thread-6,1,main]: 4", "Thread[Thread-3,1,main]: 3", "Thread[Thread-4,1,main]: 3", "Thread[Thread-5,1,main]: 3", "Thread[Thread-6,1,main]: 3", "Thread[Thread-3,1,main]: 2", "Thread[Thread-4,1,main]: 2", "Thread[Thread-5,1,main]: 2", "Thread[Thread-6,1,main]: 2", "Thread[Thread-4,1,main]: 1", "Thread[Thread-3,1,main]: 1", "Thread[Thread-6,1,main]: 1", "Thread[Thread-5,1,main]: 1" }, Test.IGNORE_ORDER + Test.WAIT); } }

  10. Daemon threads • Providing some services in background as long as program is running • When all non-daemon threads complete program execusion is terminated. public class SimpleDaemons extends Thread { public SimpleDaemons() { setDaemon(true); // Must be called before start() start(); } public void run() { while(true) { try { sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(this); } } public static void main(String[] args) { for(int i = 0; i < 10; i++) new SimpleDaemons(); } }

  11. Joining a thread • Join is a mechanism for synchronizing threads • One thread calls join on another thread. • The effect is that calling thread is suspended until the target thread finishes • It is possible to specify a timeout to the join. • The effect is that calling thread is suspnded until target thread finishes or timeout period is over, whichever comes sooner

  12. Joining a thread example class Sleeper extends Thread { private int duration; public Sleeper(String name, int sleepTime) { super(name); duration = sleepTime; start(); } public void run() { try { sleep(duration); } catch (InterruptedException e) { System.out.println(getName() + " was interrupted. " + "isInterrupted(): " + isInterrupted()); return; } System.out.println(getName() + " has awakened"); } }

  13. Joining a thread example (cont.) class Joiner extends Thread { private Sleeper sleeper; public Joiner(String name, Sleeper sleeper) { super(name); this.sleeper = sleeper; start(); } public void run() { try { sleeper.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(getName() + " join completed"); } }

  14. Joining a thread example (cont.) public class Joining { private static Test monitor = new Test(); public static void main(String[] args) { Sleeper sleepy = new Sleeper("Sleepy", 1500), grumpy = new Sleeper("Grumpy", 1500); Joiner dopey = new Joiner("Dopey", sleepy), doc = new Joiner("Doc", grumpy); grumpy.interrupt(); monitor.expect(new String[] { "Grumpy was interrupted. isInterrupted(): false", "Doc join completed", "Sleepy has awakened", "Dopey join completed" }, Test.AT_LEAST + Test.WAIT); } }

  15. Runnable interface – when a class can't inherit from thread public class RunnableThread implements Runnable { private int countDown = 5; public String toString() { return "#" + Thread.currentThread().getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 1; i <= 5; i++) new Thread(new RunnableThread(), "" + i).start(); // Output is like SimpleThread.java } }

  16. A thread as an inner class // Using a named inner class: class InnerThread1 { private int countDown = 5; private Inner inner; private class Inner extends Thread { Inner(String name) { super(name); start(); } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public String toString() { return getName() + ": " + countDown; } } public InnerThread1(String name) { inner = new Inner(name); } }

  17. Using an annonymous inner class // Using an anonymous inner class: class InnerThread2 { private int countDown = 5; private Thread t; public InnerThread2(String name) { t = new Thread(name) { public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public String toString() { return getName() + ": " + countDown; } }; t.start(); } }

  18. Using a named runnable implementation class InnerRunnable1 { private int countDown = 5; private Inner inner; private class Inner implements Runnable { Thread t; Inner(String name) { t = new Thread(this, name); t.start(); } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public String toString() { return t.getName() + ": " + countDown; } } public InnerRunnable1(String name) { inner = new Inner(name); } }

  19. Using anonymous runnable class InnerRunnable2 { private int countDown = 5; private Thread t; public InnerRunnable2(String name) { t = new Thread(new Runnable() { public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public String toString() { return Thread.currentThread().getName() + ": " + countDown; } }, name); t.start(); } }

  20. A method to run some code as thread class ThreadMethod { private int countDown = 5; private Thread t; private String name; public ThreadMethod(String name) { this.name = name; } public void runThread() { if(t == null) { t = new Thread(name) { public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; try { sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public String toString() { return getName() + ": " + countDown; } }; t.start(); } } }

  21. Creating responsive UI class UnresponsiveUI { private volatile double d = 1; public UnresponsiveUI() throws Exception { while(d > 0) d = d + (Math.PI + Math.E) / d; System.in.read(); // Never gets here } } public class ResponsiveUI extends Thread { private static Test monitor = new Test(); private static volatile double d = 1; public ResponsiveUI() { setDaemon(true); start(); } public void run() { while(true) { d = d + (Math.PI + Math.E) / d; } } public static void main(String[] args) throws Exception { //! new UnresponsiveUI(); // Must kill this process new ResponsiveUI(); Thread.sleep(300); System.in.read(); // 'monitor' provides input System.out.println(d); // Shows progress } }

  22. Improperly accessing resources public class AlwaysEven { private int i; public void next() { i++; i++; } public int getValue() { return i; } public static void main(String[] args) { final AlwaysEven ae = new AlwaysEven(); new Thread("Watcher") { public void run() { while(true) { int val = ae.getValue(); if(val % 2 != 0) { System.out.println(val); System.exit(0); } } } }.start(); while(true) ae.next(); } }

  23. Synchronizing access to shared resources • The problem in the previous program is that changing the value of “i” (shared resoruce) is not done as an atomic operation • Therefore in the middle of next(), it may be interrupted, leaving object in a inconsistent state • To solve this problem, access to the resource should be synchronized

  24. Synchronizing access example public class SynchronizedEvenGenerator implements Invariant { private int i; public synchronized void next() { i++; i++; } public synchronized int getValue() { return i; } // Not synchronized so it can run at // any time and thus be a genuine test: public InvariantState invariant() { int val = getValue(); if(val % 2 == 0) return new InvariantOK(); else return new InvariantFailure(new Integer(val)); } public static void main(String[] args) { SynchronizedEvenGenerator gen = new SynchronizedEvenGenerator(); new InvariantWatcher(gen, 4000); // 4-second timeout while(true) gen.next(); } }

  25. Critical sections • Synchronizing a method prevents access to object for full execution of the method • If a method is long then it may suspend execution of other threads for a long period • Sometimes we only need to synchronize some part of a method • This can be done by using critical sections synchronized(syncObject) { // This code can be accessed // by only one thread at a time }

  26. Critical sections example class Pair { // Not thread-safe private int x, y; public Pair(int x, int y) { this.x = x; this.y = y; } public Pair() { this(0, 0); } public int getX() { return x; } public int getY() { return y; } public void incrementX() { x++; } public void incrementY() { y++; } public String toString() { return "x: " + x + ", y: " + y; } }

  27. Critical sections example (cont.) // Protect a Pair inside a thread-safe class: abstract class PairManager { protected Pair p = new Pair(); private List storage = new ArrayList(); public synchronized Pair getPair() { // Make a copy to keep the original safe: return new Pair(p.getX(), p.getY()); } protected void store() { storage.add(getPair()); } // A "template method": public abstract void doTask(); }

  28. Critical sections example (cont.) // Synchronize the entire method: class PairManager1 extends PairManager { public synchronized void doTask() { p.incrementX(); p.incrementY(); store(); } } // Use a critical section: class PairManager2 extends PairManager { public void doTask() { synchronized(this) { p.incrementX(); p.incrementY(); } store(); } }

  29. Making a file handling utility (cont.) public TextFile(String fileName) throws IOException { super(Arrays.asList(read(fileName).split("\n"))); } public void write(String fileName) throws IOException { PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter(fileName))); for(int i = 0; i < size(); i++) out.println(get(i)); out.close(); } // Simple test: public static void main(String[] args) throws Exception { String file = read("TextFile.java"); write("test.txt", file); TextFile text = new TextFile("test.txt"); text.write("test2.txt"); } }

  30. Thread states • New: The thread object has been created, but it hasn’t been started yet, so it cannot run. • Runnable: This means that a thread can be run when the time-slicing mechanism has CPU cycles available for the thread. Thus, the thread might or might not be running at any moment, • Dead: The normal way for a thread to die is by returning from its run( ) method. • Blocked: The thread could be run, but there’s something that prevents it. While a thread is in the blocked state, the scheduler will simply skip over it and not give it any CPU time. Until a thread reenters the runnable state, it won’t perform any operations.

  31. Becoming blocked • You’ve put the thread to sleep by calling sleep(milliseconds), in which case it will not be run for the specified time. • You’ve suspended the execution of the thread with wait( ). It will not become runnable again until the thread gets the notify( ) or notifyAll( ) message. We’ll examine these in the next section. • The thread is waiting for some I/O to complete. • The thread is trying to call a synchronized method on another object, and that object’s lock is not available.

  32. Cooperation between threads – wait() and notify() class Order { private static int i = 0; private int count = i++; public Order() { if(count == 10) { System.out.println("Out of food, closing"); System.exit(0); } } public String toString() { return "Order " + count; } } class WaitPerson extends Thread { private Restaurant restaurant; public WaitPerson(Restaurant r) { restaurant = r; start(); } public void run() { while(true) { while(restaurant.order == null) synchronized(this) { try { wait(); } catch(InterruptedException e) { throw new RuntimeException(e); } } System.out.println( "Waitperson got " + restaurant.order); restaurant.order = null; } } }

  33. Cooperation between threads – wait() and notify() (cont.) class WaitPerson extends Thread { private Restaurant restaurant; public WaitPerson(Restaurant r) { restaurant = r; start(); } public void run() { while(true) { while(restaurant.order == null) synchronized(this) { try { wait(); } catch(InterruptedException e) { throw new RuntimeException(e); } } System.out.println( "Waitperson got " + restaurant.order); restaurant.order = null; } } }

  34. Cooperation between threads – wait() and notify() (cont.) class Chef extends Thread { private Restaurant restaurant; private WaitPerson waitPerson; public Chef(Restaurant r, WaitPerson w) { restaurant = r; waitPerson = w; start(); } public void run() { while(true) { if(restaurant.order == null) { restaurant.order = new Order(); System.out.print("Order up! "); synchronized(waitPerson) { waitPerson.notify(); } } try { sleep(100); } catch(InterruptedException e) { throw new RuntimeException(e); } } } }

  35. Cooperation between threads – wait() and notify() (cont.) public class Restaurant { private static Test monitor = new Test(); Order order; // Package access public static void main(String[] args) { Restaurant restaurant = new Restaurant(); WaitPerson waitPerson = new WaitPerson(restaurant); Chef chef = new Chef(restaurant, waitPerson); monitor.expect(new String[] { "Order up! Waitperson got Order 0", "Order up! Waitperson got Order 1", "Order up! Waitperson got Order 2", "Order up! Waitperson got Order 3", "Order up! Waitperson got Order 4", "Order up! Waitperson got Order 5", "Order up! Waitperson got Order 6", "Order up! Waitperson got Order 7", "Order up! Waitperson got Order 8", "Order up! Waitperson got Order 9", "Out of food, closing" }, Test.WAIT); } }

  36. Cooperation between threads – using pipes for IO between thread class Sender extends Thread { private Random rand = new Random(); private PipedWriter out = new PipedWriter(); public PipedWriter getPipedWriter() { return out; } public void run() { while(true) { for(char c = 'A'; c <= 'z'; c++) { try { out.write(c); sleep(rand.nextInt(500)); } catch(Exception e) { throw new RuntimeException(e); } } } } }

  37. Cooperation between threads – using pipes for IO between thread (cont.) class Receiver extends Thread { private PipedReader in; public Receiver(Sender sender) throws IOException { in = new PipedReader(sender.getPipedWriter()); } public void run() { try { while(true) { // Blocks until characters are there: System.out.println("Read: " + (char)in.read()); } } catch(IOException e) { throw new RuntimeException(e); } } } public class PipedIO { public static void main(String[] args) throws Exception { Sender sender = new Sender(); Receiver receiver = new Receiver(sender); sender.start(); receiver.start(); new Timeout(4000, "Terminated"); } }

  38. Deadlock • Thread synchronization and blocking, suspends some threads from execution. This way a series of threads may be waiting for another to complete its task • However, if we have a circular waiting threads none of them can continue its execution • This is called deadlock

  39. Deadlock example – dinning philosophers • We have a number of philosophers in a room • Each philosopher is in a loop of thinking and eating • There is a round dinning table with plates full of foods • There is a chopstick in left and right hand of each plate • For each philosopher to start eating, it must have both chopsticks in hand • Therefore, if all philosophers at the same time get left hand chopstick, they all can't get right hand chopstick (a deadlock situation)

  40. Deadlock example – dinning philosophers - code class Chopstick { private static int counter = 0; private int number = counter++; public String toString() { return "Chopstick " + number; } } class Philosopher extends Thread { private static Random rand = new Random(); private static int counter = 0; private int number = counter++; private Chopstick leftChopstick; private Chopstick rightChopstick; static int ponder = 0; // Package access public Philosopher(Chopstick left, Chopstick right) { leftChopstick = left; rightChopstick = right; start(); }

  41. dinning philosophers – code (cont.) public void think() { System.out.println(this + " thinking"); if(ponder > 0) try { sleep(rand.nextInt(ponder)); } catch(InterruptedException e) { throw new RuntimeException(e); } } public void eat() { synchronized(leftChopstick) { System.out.println(this + " has " + this.leftChopstick + " Waiting for " + this.rightChopstick); synchronized(rightChopstick) { System.out.println(this + " eating"); } } } public String toString() { return "Philosopher " + number; } public void run() { while(true) { think(); eat(); } } }

  42. dinning philosophers – code (cont.) public class DiningPhilosophers { public static void main(String[] args) { if(args.length < 3) { System.err.println("usage:\n" + "java DiningPhilosophers numberOfPhilosophers " + "ponderFactor deadlock timeout\n" + "A nonzero ponderFactor will generate a random " + "sleep time during think().\n" + "If deadlock is not the string " + "'deadlock', the program will not deadlock.\n" + "A nonzero timeout will stop the program after " + "that number of seconds."); System.exit(1); } Philosopher[] new Philosopher[Integer.parseInt(args[0])]; Philosopher.ponder = Integer.parseInt(args[1]); Chopstick left = new Chopstick(), right = new Chopstick(), first = left;

  43. dinning philosophers – code (cont.) int i = 0; while(i < philosopher.length - 1) { philosopher[i++] = new Philosopher(left, right); left = right; right = new Chopstick(); } if(args[2].equals("deadlock")) philosopher[i] = new Philosopher(left, first); else // Swapping values prevents deadlock: philosopher[i] = new Philosopher(first, left); // Optionally break out of program: if(args.length >= 4) { int delay = Integer.parseInt(args[3]); if(delay != 0) new Timeout(delay * 1000, "Timed out"); } } }

  44. Deadlock conditions Mutual exclusion: At least one resource used by the threads must not be shareable. In this case, a chopstick can be used by only one philosopher at a time. At least one process must be holding a resource and waiting to acquire a resource currently held by another process. That is, for deadlock to occur, a philosopher must be holding one chopstick and waiting for the other one. A resource cannot be preemptively taken away from a process. All processes must only release resources as a normal event. Our philosophers are polite and they don’t grab chopsticks from other philosophers. A circular wait must happen, whereby a process waits on a resource held by another process, which in turn is waiting on a resource held by another process, and so on, until one of the processes is waiting on a resource held by the first process

More Related