diff --git a/concurrent/CompletablePizza.java b/concurrent/CompletablePizza.java index fcac4414..5b2416e2 100644 --- a/concurrent/CompletablePizza.java +++ b/concurrent/CompletablePizza.java @@ -42,7 +42,6 @@ public class CompletablePizza { } } /* Output: -/* Output: 56 Pizza 0: ROLLED Pizza 1: ROLLED diff --git a/concurrent/DiningPhilosophers.java b/concurrent/DiningPhilosophers.java index 49e0b96d..65c9231f 100644 --- a/concurrent/DiningPhilosophers.java +++ b/concurrent/DiningPhilosophers.java @@ -2,63 +2,36 @@ // (c)2016 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. -// Deadlock can be hidden in a program -import java.util.stream.*; +// Hidden deadlock +import java.util.*; import java.util.concurrent.*; import static java.util.concurrent.TimeUnit.*; public class DiningPhilosophers { - static void stopAfter(int secs) { + private StickHolder[] sticks; + private Philosopher[] philosophers; + public DiningPhilosophers(int n) { + sticks = new StickHolder[n]; + Arrays.setAll(sticks, i -> new StickHolder()); + philosophers = new Philosopher[n]; + Arrays.setAll(philosophers, i -> + new Philosopher(i, + sticks[i], sticks[(i + 1) % n])); // [1] + // Fix by reversing stick order: + // philosophers[1] = // [2] + // new Philosopher(0, sticks[0], sticks[1]); + Arrays.stream(philosophers) + .forEach(CompletableFuture::runAsync); // [3] + } + public static void main(String[] args) { + // Returns right away: + new DiningPhilosophers(5); // [4] + // Keeps main() from exiting: ScheduledExecutorService sched = Executors.newScheduledThreadPool(1); sched.schedule( () -> { - System.out.println("Timeout"); - Philosopher.running.set(false); - sched.shutdownNow(); - System.exit(0); - }, secs, SECONDS); - } - public static void main(String[] args) - throws Exception { - stopAfter(3); - try { - IntStream.range(0, Philosopher.QUANTITY) - .mapToObj(Philosopher::new) - .map(CompletableFuture::runAsync) - .collect(Collectors.toList()) - .forEach(CompletableFuture::join); - } catch(CompletionException ex) { - Philosopher.running.set(false); - System.out.println("Broken out of deadlock"); - System.out.println("..."); - } - Philosopher.trace.stream() - .skip(Philosopher.trace.size() - 20) - .forEach(System.out::println); + System.out.println("Shutdown"); + sched.shutdown(); + }, 3, SECONDS); } } -/* Output: -Broken out of deadlock -... -P2 grabbing right -P1 grabbing left -P3 eating -P3 thinking -P3 grabbing right -P4 eating -P2 grabbing left -P4 thinking -P0 eating -P4 grabbing right -P3 grabbing left -P0 thinking -P0 grabbing right -P4 grabbing left -P0 grabbing left -P1 eating -P2 eating -P3 eating -P0 eating -P4 eating -Timeout -*/ diff --git a/concurrent/FrostedCake.java b/concurrent/FrostedCake.java index f4926324..80d77d67 100644 --- a/concurrent/FrostedCake.java +++ b/concurrent/FrostedCake.java @@ -3,7 +3,6 @@ // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. import java.util.concurrent.*; -import java.util.function.*; import java.util.stream.*; import onjava.Nap; @@ -20,6 +19,8 @@ public class FrostedCake { public FrostedCake(Baked baked, Frosting frosting) { new Nap(100); } + @Override + public String toString() { return "FrostedCake"; } public static void main(String[] args) { Baked.batch().forEach(baked -> baked .thenCombineAsync(Frosting.make(), diff --git a/concurrent/ParallelPrime.java b/concurrent/ParallelPrime.java index 3c16a8eb..fcf844ee 100644 --- a/concurrent/ParallelPrime.java +++ b/concurrent/ParallelPrime.java @@ -7,7 +7,6 @@ import java.util.stream.*; import static java.util.stream.LongStream.*; import java.io.*; import java.nio.file.*; -import java.nio.charset.*; import onjava.Timer; public class ParallelPrime { @@ -32,6 +31,5 @@ public class ParallelPrime { } } /* Output: -/* Output: 616 */ diff --git a/concurrent/Philosopher.java b/concurrent/Philosopher.java index a7c570ac..cd4d56e0 100644 --- a/concurrent/Philosopher.java +++ b/concurrent/Philosopher.java @@ -2,58 +2,29 @@ // (c)2016 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. -import java.util.*; -import java.util.stream.*; -import java.util.concurrent.*; -import static java.util.concurrent.TimeUnit.*; -import java.util.concurrent.atomic.*; -class Philosopher implements Runnable { - static class Chopstick {} - public static final int QUANTITY = 5; - static Queue trace = - new ConcurrentLinkedQueue<>(); - static AtomicBoolean running = - new AtomicBoolean(true); - public static - List> chopsticks = - IntStream.range(0, Philosopher.QUANTITY) - .mapToObj(i -> { - BlockingQueue bd = - new ArrayBlockingQueue<>(1); - bd.add(new Chopstick()); - return bd; - }) - .collect(Collectors.toList()); - private final int seatNumber; - private final int left, right; - public Philosopher(int seatNumber) { - this.seatNumber = left = seatNumber; - right = (seatNumber + 1) % QUANTITY; - } - @Override - public void run() { - try { - while(running.get()) { - trace.add(this + " thinking"); - // Philosopher becomes hungry - trace.add(this + " grabbing right"); - Chopstick rightStick = - chopsticks.get(right).poll(2, SECONDS); - trace.add(this + " grabbing left"); - Chopstick leftStick = - chopsticks.get(left).poll(2, SECONDS); - trace.add(this + " eating"); - // Finished, return chopsticks to table: - chopsticks.get(right).put(rightStick); - chopsticks.get(left).put(leftStick); - } - } catch(InterruptedException e) { - trace.add("exiting via interrupt"); - } +public class Philosopher implements Runnable { + private final int seat; + private final StickHolder left, right; + public Philosopher(int seat, + StickHolder left, StickHolder right) { + this.seat = seat; + this.left = left; + this.right = right; } @Override public String toString() { - return "P" + seatNumber; + return "P" + seat; + } + @Override + public void run() { + while(true) { + // System.out.println("Thinking"); // [1] + right.pickUp(); + left.pickUp(); + System.out.println(this + " eating"); + right.putDown(); + left.putDown(); + } } } diff --git a/concurrent/StickHolder.java b/concurrent/StickHolder.java new file mode 100644 index 00000000..456da253 --- /dev/null +++ b/concurrent/StickHolder.java @@ -0,0 +1,27 @@ +// concurrent/StickHolder.java +// (c)2016 MindView LLC: see Copyright.txt +// We make no guarantees that this code is fit for any purpose. +// Visit http://OnJava8.com for more book information. +import java.util.concurrent.*; + +public class StickHolder { + private static class Chopstick {} + private Chopstick stick = new Chopstick(); + private BlockingQueue holder = + new ArrayBlockingQueue<>(1); + public StickHolder() { putDown(); } + public void pickUp() { + try { + holder.take(); // Blocks if unavailable + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + } + public void putDown() { + try { + holder.put(stick); + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + } +}