├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── com └── packt └── tfesenko └── multithreading ├── section1 ├── AppleTree.java ├── Lesson2.java ├── Lesson3.java ├── Lesson4.java └── Lesson5.java ├── section2 ├── Lesson1.java ├── Lesson2.java ├── Lesson3.java ├── Lesson4.java └── Lesson5.java ├── section3 ├── Lesson2.java ├── Lesson3.java └── Lesson4.java ├── section4 ├── Lesson1.java ├── Lesson2.java ├── Lesson3.java └── Lesson4.java └── section5 ├── Lesson1.java ├── Lesson2.java ├── Lesson3.java └── Lesson4.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Java Concurrency and Multithreading in Practice 5 | This is the code repository for [Java Concurrency and Multithreading in Practice]( https://www.packtpub.com/application-development/java-concurrency-and-multithreading-practice-video), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the video course from start to finish. 6 | ## About the Video Course 7 | Multi-core processors are everywhere—from super-computers to mobile devices right in your pocket. That's why a modern developer must know how to leverage the power of multithreading. 8 | This course will teach you how to use parallelism and concurrency in Java. You will learn how to parallelize tasks and functions with the ForkJoin framework and Streams. You will also learn how to work with the very popular Reactive Streams recently introduced to Java. Furthermore, you will master concurrent collections and lower-level synchronization techniques with locks. 9 | 10 | This course conveniently provides quizzes to evaluate your knowledge and help you retain the new concepts. 11 | 12 | By the end of this practical training course, you will have the knowledge to write elegant programs for multicore computers with enhanced performance and improved responsiveness. 13 | 14 |

What You Will Learn

15 |
16 |
22 | 23 | ## Instructions and Navigation 24 | ### Assumed Knowledge 25 | If you are a Java developer keen to learn how to write modern parallel and concurrent programs to improve application performance, this course is for you. Some basic workable knowledge of Java is assumed. 26 | ### Technical Requirements 27 | 34 | 35 | 42 | 43 | 48 | 49 | 50 | 51 | ## Related Products 52 | * [Mastering Odoo 12 Development [Video]](https://www.packtpub.com/business/mastering-odoo-12-development-video?utm_source=github&utm_medium=repository&utm_campaign=9781789139280) 53 | 54 | * [High-Performance Computing with Python 3.x [Video]](https://www.packtpub.com/application-development/high-performance-computing-python-3x-video?utm_source=github&utm_medium=repository&utm_campaign=9781789956252) 55 | 56 | * [Functional Programming in 7 Days [Video]](https://www.packtpub.com/application-development/functional-programming-7-days-video?utm_source=github&utm_medium=repository&utm_campaign=9781788990295) 57 | 58 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | com.packt.tfesenko 6 | concurrency 7 | 0.0.1-SNAPSHOT 8 | Java Concurrency and Multithreading in Practice 9 | Java Concurrency and Multithreading in Practice 10 | 11 | 1.9 12 | 1.9 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section1/AppleTree.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section1; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class AppleTree { 8 | 9 | public static AppleTree[] newTreeGarden(int size) { 10 | AppleTree[] appleTrees = new AppleTree[size]; 11 | for (int i = 0; i < appleTrees.length; i++) { 12 | appleTrees[i] = new AppleTree("🌳#" + i); 13 | } 14 | return appleTrees; 15 | } 16 | 17 | private final String treeLabel; 18 | private final int numberOfApples; 19 | 20 | public AppleTree(String treeLabel) { 21 | this.treeLabel = treeLabel; 22 | numberOfApples = 3; 23 | } 24 | 25 | public int pickApples(String workerName) { 26 | try { 27 | //System.out.printf("%s started picking apples from %s \n", workerName, treeLabel); 28 | TimeUnit.SECONDS.sleep(1); 29 | System.out.printf("%s picked %d 🍏s from %s \n", workerName, numberOfApples, treeLabel); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | return numberOfApples; 34 | } 35 | 36 | public int pickApples() { 37 | return pickApples(toLabel(Thread.currentThread().getName())); 38 | } 39 | 40 | private String toLabel(String threadName) { 41 | HashMap threadNameToLabel = new HashMap<>(); 42 | threadNameToLabel.put("ForkJoinPool.commonPool-worker-1", "Alice"); 43 | threadNameToLabel.put("ForkJoinPool.commonPool-worker-2", "Bob"); 44 | threadNameToLabel.put("ForkJoinPool.commonPool-worker-3", "Carol"); 45 | threadNameToLabel.put("ForkJoinPool.commonPool-worker-4", "Dan"); 46 | 47 | return threadNameToLabel.getOrDefault(threadName, threadName); 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section1/Lesson2.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section1; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.ForkJoinPool; 7 | 8 | public class Lesson2 { 9 | public static void main(String[] args) { 10 | AppleTree[] appleTrees = AppleTree.newTreeGarden(6); 11 | 12 | Callable applePicker1 = createApplePicker(appleTrees, 0, 2, "Alex"); 13 | Callable applePicker2 = createApplePicker(appleTrees, 2, 4, "Bob"); 14 | Callable applePicker3 = createApplePicker(appleTrees, 4, 6, "Carol"); 15 | 16 | ForkJoinPool.commonPool().invokeAll(asList(applePicker1, applePicker2, applePicker3)); 17 | 18 | System.out.println(); 19 | System.out.println("All fruits collected!"); 20 | } 21 | 22 | public static Callable createApplePicker(AppleTree[] appleTrees, int fromIndexInclusive, int toIndexExclusive, 23 | String workerName) { 24 | return () -> { 25 | for (int i = fromIndexInclusive; i < toIndexExclusive; i++) { 26 | appleTrees[i].pickApples(workerName); 27 | } 28 | return null; 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section1/Lesson3.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section1; 2 | 3 | import java.util.concurrent.ForkJoinPool; 4 | import java.util.concurrent.RecursiveTask; 5 | import java.util.stream.IntStream; 6 | 7 | public class Lesson3 { 8 | 9 | public static void main(String[] args) { 10 | AppleTree[] appleTrees = AppleTree.newTreeGarden(12); 11 | ForkJoinPool pool = ForkJoinPool.commonPool(); 12 | 13 | PickFruitTask task = new PickFruitTask(appleTrees, 0, appleTrees.length - 1); 14 | int result = pool.invoke(task); 15 | 16 | System.out.println(); 17 | System.out.println("Total apples picked: " + result); 18 | } 19 | 20 | public static class PickFruitTask extends RecursiveTask { 21 | 22 | private final AppleTree[] appleTrees; 23 | private final int startInclusive; 24 | private final int endInclusive; 25 | 26 | private final int taskThreadshold = 4; 27 | 28 | public PickFruitTask(AppleTree[] array, int startInclusive, int endInclusive) { 29 | this.appleTrees = array; 30 | this.startInclusive = startInclusive; 31 | this.endInclusive = endInclusive; 32 | } 33 | 34 | @Override 35 | protected Integer compute() { 36 | if (endInclusive - startInclusive < taskThreadshold) { 37 | return doCompute(); 38 | } 39 | int midpoint = startInclusive + (endInclusive - startInclusive) / 2; 40 | 41 | PickFruitTask leftSum = new PickFruitTask(appleTrees, startInclusive, midpoint); 42 | PickFruitTask rightSum = new PickFruitTask(appleTrees, midpoint + 1, endInclusive); 43 | 44 | rightSum.fork(); // computed asynchronously 45 | 46 | return leftSum.compute()// computed synchronously: immediately and in the current thread 47 | + rightSum.join(); 48 | } 49 | 50 | protected Integer doCompute() { 51 | return IntStream.rangeClosed(startInclusive, endInclusive)// 52 | .map(i -> appleTrees[i].pickApples())// 53 | .sum(); 54 | 55 | // Equivalent with a "for" loop :) 56 | // int result = 0; 57 | // for (int i = startInclusive; i <= endInclusive; i++) { 58 | // result += array[i].pickApples(); 59 | // } 60 | // return result; 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section1/Lesson4.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section1; 2 | 3 | import java.util.concurrent.ForkJoinPool; 4 | import java.util.concurrent.RecursiveAction; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.stream.IntStream; 7 | 8 | public class Lesson4 { 9 | 10 | public static void main(String[] args) throws InterruptedException { 11 | AppleTree[] appleTrees = AppleTree.newTreeGarden(12); 12 | PickFruitAction task = new PickFruitAction(appleTrees, 0, appleTrees.length - 1); 13 | 14 | ForkJoinPool pool = ForkJoinPool.commonPool(); 15 | 16 | pool.invoke(task); 17 | // try this: pool.execute(task); 18 | // try this: pool.execute(task); task.join(); 19 | // try this: pool.execute(task); pool.awaitTermination(10, TimeUnit.SECONDS); 20 | 21 | System.out.println(); 22 | System.out.println("Done!"); 23 | } 24 | 25 | public static class PickFruitAction extends RecursiveAction { 26 | 27 | private final AppleTree[] appleTrees; 28 | private final int startInclusive; 29 | private final int endInclusive; 30 | 31 | private final int taskThreadshold = 4; 32 | 33 | public PickFruitAction(AppleTree[] array, int startInclusive, int endInclusive) { 34 | this.appleTrees = array; 35 | this.startInclusive = startInclusive; 36 | this.endInclusive = endInclusive; 37 | } 38 | 39 | @Override 40 | protected void compute() { 41 | if (endInclusive - startInclusive < taskThreadshold) { 42 | doCompute(); 43 | return; 44 | } 45 | int midpoint = startInclusive + (endInclusive - startInclusive) / 2; 46 | 47 | PickFruitAction leftSum = new PickFruitAction(appleTrees, startInclusive, midpoint); 48 | PickFruitAction rightSum = new PickFruitAction(appleTrees, midpoint + 1, endInclusive); 49 | 50 | rightSum.fork(); // computed asynchronously 51 | leftSum.compute();// computed synchronously: immediately and in the current thread 52 | rightSum.join(); 53 | } 54 | 55 | protected void doCompute() { 56 | IntStream.rangeClosed(startInclusive, endInclusive)// 57 | .forEach(i -> appleTrees[i].pickApples()); 58 | 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section1/Lesson5.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section1; 2 | 3 | import java.util.concurrent.ForkJoinPool; 4 | import java.util.concurrent.RecursiveTask; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.stream.IntStream; 7 | 8 | public class Lesson5 { 9 | 10 | private static final int treeNumber = 12; 11 | 12 | public static void main(String[] args) { 13 | AppleTree[] appleTrees = AppleTree.newTreeGarden(treeNumber); 14 | ForkJoinPool pool = ForkJoinPool.commonPool(); 15 | 16 | PickFruitTask task = new PickFruitTask(appleTrees, 0, appleTrees.length - 1); 17 | int result = pool.invoke(task); 18 | 19 | System.out.println(); 20 | System.out.println("Total apples picked: " + result); 21 | } 22 | 23 | public static class SomethingWentWrongException extends Exception { 24 | } 25 | 26 | public static class PickFruitTask extends RecursiveTask { 27 | 28 | private final AppleTree[] appleTrees; 29 | private final int startInclusive; 30 | private final int endInclusive; 31 | 32 | private final int taskThreadshold = 4; 33 | 34 | public PickFruitTask(AppleTree[] array, int startInclusive, int endInclusive) { 35 | this.appleTrees = array; 36 | this.startInclusive = startInclusive; 37 | this.endInclusive = endInclusive; 38 | } 39 | 40 | @Override 41 | protected Integer compute() { 42 | // throw an exception for any task from the right side of the array 43 | if (startInclusive >= treeNumber / 2) { 44 | // try this: int throwException = 10/0; 45 | // try this: throw new SomethingWentWrongException(); 46 | // try this: completeExceptionally(new SomethingWentWrongException()); 47 | } 48 | if (endInclusive - startInclusive < taskThreadshold) { 49 | return doCompute(); 50 | } 51 | int midpoint = startInclusive + (endInclusive - startInclusive) / 2; 52 | 53 | PickFruitTask leftSum = new PickFruitTask(appleTrees, startInclusive, midpoint); 54 | PickFruitTask rightSum = new PickFruitTask(appleTrees, midpoint + 1, endInclusive); 55 | 56 | rightSum.fork(); // computed asynchronously 57 | 58 | try { 59 | TimeUnit.MILLISECONDS.sleep(100); 60 | } catch (InterruptedException e) { 61 | // TODO Auto-generated catch block 62 | e.printStackTrace(); 63 | } 64 | 65 | // try this: rightSum.cancel(true); 66 | 67 | return leftSum.compute()// computed synchronously: immediately and in the current thread 68 | + rightSum.join(); 69 | } 70 | 71 | protected Integer doCompute() { 72 | return IntStream.rangeClosed(startInclusive, endInclusive)// 73 | .map(i -> appleTrees[i].pickApples())// 74 | .sum(); 75 | 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section2/Lesson1.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section2; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | import java.util.concurrent.Future; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class Lesson1 { 10 | 11 | public static void main(String[] args) throws InterruptedException, ExecutionException { 12 | demoFutureWithCallable(); 13 | demoCallableVsRunnable(); 14 | } 15 | 16 | public static void demoFutureWithCallable() throws InterruptedException, ExecutionException { 17 | System.out.println(); 18 | System.out.println("Demo Future with Callable"); 19 | ExecutorService pool = Executors.newCachedThreadPool(); 20 | 21 | Future pizzaPickupOrder = pool.submit(() -> { 22 | System.out.println(" Restaurant> Slicing tomatoes"); 23 | System.out.println(" Restaurant> Chopping onions"); 24 | System.out.println(" Restaurant> Spreading with tomato sauce and sprinkle with toppings"); 25 | System.out.println(" Restaurant> Baking pizza"); 26 | TimeUnit.MILLISECONDS.sleep(300); 27 | return new Pizza(); 28 | }); 29 | 30 | System.out.println("Me: Call my brother"); 31 | TimeUnit.MILLISECONDS.sleep(200); 32 | System.out.println("Me: Walk the dog"); 33 | 34 | // Try this: pizzaPickupOrder.cancel(true); 35 | if (pizzaPickupOrder.isCancelled()) { 36 | System.out.println("Me: pizza is cancelled, order something else"); 37 | System.out.println("pizzaPickupOrder.isDone(): " + pizzaPickupOrder.isDone()); 38 | } else if (!pizzaPickupOrder.isDone()) { 39 | System.out.println("Me: Watch a TV show"); 40 | } 41 | Pizza pizza = pizzaPickupOrder.get(); 42 | 43 | System.out.println("Me: Eat the pizza: " + pizza); 44 | 45 | pool.shutdown(); 46 | System.out.println(); 47 | System.out.println(); 48 | } 49 | 50 | public static void demoCallableVsRunnable() throws InterruptedException, ExecutionException { 51 | System.out.println(); 52 | System.out.println("Demo: Callable vs Runnable"); 53 | ExecutorService pool = Executors.newCachedThreadPool(); 54 | 55 | Runnable makePizza = () -> { 56 | System.out.println(" Restaurant> Slicing tomatoes"); 57 | System.out.println(" Restaurant> Chopping onions"); 58 | System.out.println(" Restaurant> Spreading with tomato sauce and sprinkle with toppings"); 59 | System.out.println(" Restaurant> Baking pizza"); 60 | // Compare to Callable: need to handle exception here 61 | try { 62 | TimeUnit.MILLISECONDS.sleep(300); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | // Compare to Callable: nothing to return 67 | }; 68 | 69 | // compare to submit(Callable): Future here vs Future there 70 | Future pizzaPickupOrder = pool.submit(makePizza); 71 | 72 | // try this: pool.execute(makePizza); 73 | 74 | System.out.println("Me: Calling my brother"); 75 | TimeUnit.MILLISECONDS.sleep(200); 76 | System.out.println("Me: Walk the dog"); 77 | 78 | Object pizza = pizzaPickupOrder.get(); // null 79 | System.out.println("Me: Eat the pizza: " + pizza); 80 | 81 | pool.shutdown(); 82 | } 83 | 84 | public static class Pizza { 85 | 86 | @Override 87 | public String toString() { 88 | return "Classic margherita"; 89 | } 90 | 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section2/Lesson2.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section2; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.Future; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.ScheduledFuture; 10 | import java.util.concurrent.ThreadFactory; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | public class Lesson2 { 15 | 16 | private static final Runnable helloTask = // 17 | () -> System.out.printf("Hello from '%s'\n", Thread.currentThread().getName()); 18 | 19 | public static void main(String[] args) throws InterruptedException, ExecutionException { 20 | // a program already has a thread - the main thread 21 | System.out.println("Current thread: " + Thread.currentThread().getName()); 22 | 23 | demoThread(); 24 | demoThreadsCreatedByThreadPool(); 25 | demoDifferentExecutorServices(); 26 | demoScheduledExecutorService(); 27 | demoThreadFactory(); 28 | } 29 | 30 | public static void demoThread() { 31 | System.out.println("Demo Thread"); 32 | System.out.println( 33 | "⚠️For demo purpoeses only, don't create/start Threads yourself - use ExecutorService instead!!"); 34 | 35 | // submit 10 similar tasks 36 | for (int i = 0; i < 10; i++) { 37 | new Thread(helloTask).start(); 38 | } 39 | // The tasks are executed from _ten_ _different_ threads 40 | // 10 > 4 (4 is number of cores of my computer) 41 | // threads are NOT re-used 42 | 43 | System.out.println(); 44 | } 45 | 46 | public static void demoThreadsCreatedByThreadPool() throws InterruptedException, ExecutionException { 47 | System.out.println("Demo ThreadPool"); 48 | System.out.println("😄Use an ExecutorService to manage threads"); 49 | 50 | ExecutorService pool = Executors.newCachedThreadPool(); 51 | // submit 10 similar tasks and watch that they are executed from different 52 | // threads 53 | for (int i = 0; i < 10; i++) { 54 | pool.submit(helloTask); 55 | } 56 | 57 | // Unlike thread.start(), threadPool.submit() returns a Future 58 | Future randomNumber = pool.submit(() -> new Random().nextInt()); 59 | System.out.println("Random number: " + randomNumber.get()); 60 | 61 | pool.shutdown(); 62 | System.out.println(); 63 | } 64 | 65 | public static void demoThreadFactory() { 66 | System.out.println("Demo ThreadFactory"); 67 | System.out.println("😄Use an ExecutorService to manage threads"); 68 | 69 | ThreadFactory threadFactory = new ThreadFactory() { 70 | private final AtomicInteger threadNumber = new AtomicInteger(1); 71 | 72 | public Thread newThread(Runnable r) { 73 | Thread thread = new Thread(r); 74 | thread.setName("Hello Thread " + threadNumber.getAndIncrement()); 75 | thread.setPriority(Thread.MAX_PRIORITY); 76 | return thread; 77 | } 78 | }; 79 | 80 | ExecutorService pool = Executors.newCachedThreadPool(threadFactory); 81 | 82 | // submit 10 similar tasks and watch that they are executed from different 83 | // threads 84 | for (int i = 0; i < 10; i++) { 85 | pool.submit(helloTask); 86 | } 87 | 88 | pool.shutdown(); 89 | System.out.println(); 90 | } 91 | 92 | public static void demoDifferentExecutorServices() { 93 | System.out.println("Demo different thread pools"); 94 | 95 | ExecutorService pool = Executors.newCachedThreadPool(); 96 | // Try using these thread pools an how it influences the threads where the tasks 97 | // are executed 98 | // ExecutorService pool = Executors.newFixedThreadPool(5); 99 | // ExecutorService pool = Executors.newFixedThreadPool(1); 100 | // ExecutorService pool = Executors.newSingleThreadExecutor(); 101 | 102 | // submit 10 similar tasks and watch that they are executed from different 103 | // threads 104 | for (int i = 0; i < 10; i++) { 105 | // Unlike thread.start(), threadPool.submit() returns a Future 106 | Future result = pool.submit(helloTask); 107 | } 108 | 109 | // make sure to shut down the pool when finished using it! 110 | pool.shutdown(); 111 | System.out.println(); 112 | } 113 | 114 | public static void demoScheduledExecutorService() { 115 | System.out.println("Demo scheduled tasks"); 116 | 117 | ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4); 118 | 119 | ScheduledFuture waterReminder = scheduler.scheduleAtFixedRate( 120 | () -> System.out.println("Hi there, it's time to drink a glass of water"), // 121 | 0, 1, TimeUnit.SECONDS); 122 | 123 | ScheduledFuture exerciseReminder = scheduler.scheduleAtFixedRate( 124 | () -> System.out.println("Hi there, it's time to exercise"), // 125 | 0, 12, TimeUnit.SECONDS); 126 | 127 | // to cancel the tasks after certain amount of time 128 | Runnable canceller = () -> { 129 | exerciseReminder.cancel(false); 130 | waterReminder.cancel(false); 131 | }; 132 | scheduler.schedule(canceller, 15, TimeUnit.SECONDS); 133 | } 134 | } -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section2/Lesson3.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section2; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class Lesson3 { 10 | 11 | public static void main(String[] args) throws InterruptedException, ExecutionException { 12 | // executor is not provided, CompletableFuture.supplyAsync will use ForkJoinPool by default 13 | ExecutorService executor = Executors.newCachedThreadPool(); 14 | 15 | 16 | final String tomatoes = "Tomatoes"; 17 | CompletableFuture sliceTomatoes = CompletableFuture.supplyAsync(() -> { 18 | // try { 19 | // TimeUnit.MILLISECONDS.sleep(10); 20 | // } catch (InterruptedException e) { 21 | // e.printStackTrace(); 22 | // } 23 | System.out.println(" Restaurant> Slicing tomatoes"); 24 | if (tomatoes == null) { 25 | throw new RuntimeException("No tomatoes"); 26 | } 27 | return "Tomatoes "; 28 | }, executor).handle((result, e) -> { 29 | if (result == null) { 30 | System.out.println("Problems with tomatoes"); 31 | return ""; 32 | } 33 | return result; 34 | }); 35 | 36 | CompletableFuture chopOnions = CompletableFuture.supplyAsync(() -> { 37 | System.out.println(" Restaurant> Chopping onions"); 38 | return "Onions "; 39 | }, executor); 40 | 41 | CompletableFuture prepIngredients = sliceTomatoes.thenCombine(chopOnions, // 42 | String::concat); 43 | 44 | CompletableFuture prepPizza = prepIngredients.thenApply(toppings -> { 45 | System.out.println(" Restaurant> Spreading with tomato sauce and sprinkle with toppings: " + toppings); 46 | return "Raw pizza with " + toppings; 47 | }); 48 | 49 | CompletableFuture bakePizza = prepPizza.thenApply(rawPizza -> { 50 | System.out.println(" Restaurant> Baking pizza: " + rawPizza); 51 | try { 52 | TimeUnit.MILLISECONDS.sleep(300); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | return "Pizza"; 57 | }); 58 | 59 | bakePizza.thenAccept(pizza -> System.out.println("Eating pizza: " + pizza)); 60 | // or, the old way // System.out.println(bakePizza.get()); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section2/Lesson4.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section2; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Random; 6 | import java.util.concurrent.ExecutionException; 7 | import java.util.concurrent.ForkJoinPool; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.IntStream; 10 | import java.util.stream.LongStream; 11 | import java.util.stream.Stream; 12 | 13 | public class Lesson4 { 14 | 15 | public static void main(String[] args) { 16 | // General pattern: 17 | // 1. Create stream 18 | // 2. Optional: Intermediate Operation aka Transformation 19 | // 3. Terminal Operation aka Action 20 | 21 | demoStreamOf(); 22 | demoListToStream(); 23 | demoArrayToStream(); 24 | demoRange(); 25 | demoIntermediateOperationsAreLazy(); 26 | } 27 | 28 | public static void demoListToStream() { 29 | System.out.println("Cat count (List): "); 30 | 31 | // List.of() was introduced in Java 9, use Arrays.asList for earlier versions 32 | List catsList = List.of("Fluffy, the cat", // cat 33 | "Caesar, the dog", // 34 | "Eclair, the cat", // cat 35 | "Ella, the dog", // 36 | "Spots, the cat"// cat 37 | ); 38 | int catCountOld = 0; 39 | for (String cat : catsList) { 40 | if (cat.endsWith("the cat")) { 41 | catCountOld++; 42 | } 43 | } 44 | System.out.print("\t"); 45 | System.out.println(catCountOld); 46 | 47 | // Create a stream: from a list 48 | long catCount = catsList.stream() 49 | 50 | // filter() is an _intermediate_ operation 51 | .filter(pet -> pet.endsWith("the cat")) 52 | 53 | // count() is a _terminal_ operation 54 | .count(); 55 | 56 | System.out.print("\t"); 57 | System.out.println(catCount); 58 | System.out.println(); 59 | } 60 | 61 | public static void demoArrayToStream() { 62 | System.out.println("Cat count (Array): "); 63 | 64 | String[] cats = { "Fluffy, the cat", // cat 65 | "Caesar, the dog", // 66 | "Eclair, the cat", // cat 67 | "Ella, the dog", // 68 | "Spots, the cat"// cat 69 | }; 70 | 71 | int catCountOld = 0; 72 | for (String cat : cats) { 73 | if (cat.endsWith("the cat")) { 74 | catCountOld++; 75 | } 76 | } 77 | System.out.print("\t"); 78 | System.out.println(catCountOld); 79 | 80 | // Create a stream: from a list 81 | long catCount = Arrays.stream(cats) // 82 | 83 | // filter() is an _intermediate_ operation 84 | .filter(pet -> pet.endsWith("the cat")) 85 | 86 | // count() is a _terminal_ operation 87 | .count(); 88 | 89 | System.out.print("\t"); 90 | System.out.println(catCount); 91 | System.out.println(); 92 | } 93 | 94 | public static void demoStreamOf() { 95 | System.out.println("Cat count (Stream.of)"); 96 | // Create a stream 97 | long catCount = Stream.of(// 98 | "Fluffy, the cat", // cat 99 | "Caesar, the dog", // 100 | "Eclair, the cat", // cat 101 | "Ella, the dog", // 102 | "Spots, the cat"// cat 103 | )// 104 | // filter() is an _intermediate_ operation 105 | .filter(pet -> pet.endsWith("the cat")) 106 | // count() is a _terminal_ operation 107 | .count(); 108 | System.out.println("\t" + catCount); 109 | System.out.println(); 110 | } 111 | 112 | public static void demoRange() { 113 | int[] array = generateArrayWithRandomNumbers(1000); 114 | int startIndex = 10; 115 | int endIndex = 900; 116 | 117 | System.out.println("Sum elements in array start from from index 10 up to 900:"); 118 | // without streams 119 | int sumWithFor = 0; 120 | for (int i = startIndex; i < endIndex; i++) { 121 | sumWithFor += array[i]; 122 | } 123 | System.out.print("\t"); 124 | System.out.println(sumWithFor); 125 | 126 | // the same with streams 127 | System.out.print("\t"); 128 | 129 | // Note that this is an IntStream - a stream of _unboxed_ int type 130 | int sumWithStream = IntStream.range(startIndex, endIndex)// 131 | .map(i -> array[i])// 132 | .sum(); 133 | System.out.println(sumWithStream); 134 | System.out.println(); 135 | } 136 | 137 | public static void demoIntermediateOperationsAreLazy() { 138 | int[] array = generateArrayWithRandomNumbers(1000); 139 | 140 | System.out.println("Print elements of an array:"); 141 | IntStream.range(0, 1000)// 142 | .map(i -> { 143 | System.out.println(array[i]); 144 | return array[i]; 145 | }); 146 | // try commenting next line 147 | // .sum(); 148 | 149 | // Solution: use forEach() - a terminal operation 150 | IntStream.range(0, 1000)// 151 | .forEach(i -> System.out.println(array[i])); 152 | System.out.println(); 153 | } 154 | 155 | private static int[] generateArrayWithRandomNumbers(int arrayLength) { 156 | return new Random().ints(arrayLength, 1, 1000).toArray(); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section2/Lesson5.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section2; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.concurrent.ConcurrentMap; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.Stream; 8 | 9 | public class Lesson5 { 10 | 11 | public static void main(String[] args) { 12 | Stream.of("One", "Two", "Three") 13 | // to parallel stream 14 | .parallel(); 15 | 16 | // Or, create directly from a list 17 | List.of("January", "February", "March") 18 | // parallelStream() instead of stream() 19 | .parallelStream(); 20 | 21 | // array - parallel() from a stream 22 | String[] array = {"Coffee", "Capuccino", "Latte"}; 23 | Arrays.stream(array).parallel(); 24 | 25 | // parallel reduction using/producing a concurrent map 26 | ConcurrentMap> coffeeByFirstLetter = 27 | Arrays.stream(array).parallel()// 28 | .collect(Collectors.groupingByConcurrent(drink->drink.charAt(0))); 29 | System.out.println(coffeeByFirstLetter); 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section3/Lesson2.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section3; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.Flow; 6 | import java.util.concurrent.Flow.Subscriber; 7 | import java.util.concurrent.Flow.Subscription; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.ScheduledFuture; 10 | import java.util.concurrent.ScheduledThreadPoolExecutor; 11 | import java.util.concurrent.SubmissionPublisher; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public class Lesson2 { 15 | 16 | public static void main(String[] args) { 17 | SubmissionPublisher weatherForecastPublisher = new WeatherForecastPublisher(); 18 | 19 | Subscriber twitterSubscriber = new Flow.Subscriber() { 20 | private Flow.Subscription subscription; 21 | private final String name = "Twitter Subscriber"; 22 | 23 | @Override 24 | public void onSubscribe(Subscription subscription) { 25 | System.out.println(name + " subscribed!"); 26 | this.subscription = subscription; 27 | subscription.request(1); 28 | } 29 | 30 | @Override 31 | public void onNext(WeatherForecast weatherForecast) { 32 | System.out.println(Thread.currentThread().getName() + " > Twitting: " + weatherForecast); 33 | // You can alternatively use subscription.request(Long.MAX_VALUE) in onSubscribe() 34 | subscription.request(1); 35 | } 36 | 37 | @Override 38 | public void onError(Throwable throwable) { 39 | System.err.println(name + " got an error: " + throwable.getMessage()); 40 | } 41 | 42 | @Override 43 | public void onComplete() { 44 | System.out.println(name + " completed."); 45 | } 46 | }; 47 | 48 | weatherForecastPublisher.subscribe(twitterSubscriber); 49 | 50 | weatherForecastPublisher.subscribe(new Flow.Subscriber() { 51 | private Flow.Subscription subscription; 52 | private final String name = "Database Subscriber"; 53 | 54 | @Override 55 | public void onSubscribe(Subscription subscription) { 56 | System.out.println(name + " subscribed!"); 57 | this.subscription = subscription; 58 | subscription.request(1); 59 | } 60 | 61 | @Override 62 | public void onNext(WeatherForecast weatherForecast) { 63 | System.out.println(Thread.currentThread().getName() + " > Saving to DB: " + weatherForecast); 64 | 65 | try { 66 | TimeUnit.MILLISECONDS.sleep(1000); 67 | } catch (InterruptedException e) { 68 | e.printStackTrace(); 69 | } 70 | subscription.request(1); 71 | } 72 | 73 | @Override 74 | public void onError(Throwable throwable) { 75 | } 76 | 77 | @Override 78 | public void onComplete() { 79 | } 80 | }); 81 | 82 | // close the publisher and associated resources after 10 seconds 83 | try { 84 | TimeUnit.SECONDS.sleep(10); 85 | } catch (InterruptedException e) { 86 | e.printStackTrace(); 87 | } 88 | weatherForecastPublisher.close(); 89 | } 90 | 91 | /** 92 | * Modification of the example from JavaDoc for 93 | * java.util.concurrent.SubmissionPublisher 94 | */ 95 | public static class WeatherForecastPublisher extends SubmissionPublisher { 96 | final ScheduledFuture periodicTask; 97 | final ScheduledExecutorService scheduler; 98 | 99 | WeatherForecastPublisher() { 100 | super(Executors.newFixedThreadPool(2), Flow.defaultBufferSize()); 101 | scheduler = new ScheduledThreadPoolExecutor(1); 102 | periodicTask = scheduler.scheduleAtFixedRate( // 103 | // runs submit() 104 | () -> submit(WeatherForecast.nextRandomWeatherForecast()), // 105 | 500, 500, TimeUnit.MILLISECONDS); 106 | } 107 | 108 | public void close() { 109 | periodicTask.cancel(false); 110 | scheduler.shutdown(); 111 | super.close(); 112 | } 113 | } 114 | 115 | /** 116 | * Weather Forecast in United States customary units 117 | */ 118 | public static class WeatherForecast { 119 | private final int temperatureInF; 120 | private final int windSpeedInMPH; 121 | private final String weatherCondition; 122 | 123 | private static final Random random = new Random(); 124 | private static final String[] allWeatherConditions = new String[] { "☁️", "☀️", "⛅", "🌧", "⛈️" }; 125 | 126 | public static WeatherForecast nextRandomWeatherForecast() { 127 | String weatherCondition = allWeatherConditions[random.nextInt(allWeatherConditions.length)]; 128 | int temperatureInF = random.nextInt(95); 129 | int windSpeedInMPH = 5 + random.nextInt(30); 130 | return new WeatherForecast(temperatureInF, windSpeedInMPH, weatherCondition); 131 | } 132 | 133 | public WeatherForecast(int temperatureInF, int windSpeedInMPH, String weatherCondition) { 134 | super(); 135 | this.temperatureInF = temperatureInF; 136 | this.windSpeedInMPH = windSpeedInMPH; 137 | this.weatherCondition = weatherCondition; 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return weatherCondition + " " + temperatureInF + "°F wind: " + windSpeedInMPH + "mph"; 143 | } 144 | 145 | @Override 146 | public int hashCode() { 147 | final int prime = 31; 148 | int result = 1; 149 | result = prime * result + temperatureInF; 150 | result = prime * result + ((weatherCondition == null) ? 0 : weatherCondition.hashCode()); 151 | result = prime * result + windSpeedInMPH; 152 | return result; 153 | } 154 | 155 | @Override 156 | public boolean equals(Object obj) { 157 | if (this == obj) 158 | return true; 159 | if (obj == null) 160 | return false; 161 | if (getClass() != obj.getClass()) 162 | return false; 163 | WeatherForecast other = (WeatherForecast) obj; 164 | if (temperatureInF != other.temperatureInF) 165 | return false; 166 | if (weatherCondition == null) { 167 | if (other.weatherCondition != null) 168 | return false; 169 | } else if (!weatherCondition.equals(other.weatherCondition)) 170 | return false; 171 | if (windSpeedInMPH != other.windSpeedInMPH) 172 | return false; 173 | return true; 174 | } 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section3/Lesson3.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section3; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | import java.util.concurrent.Flow; 7 | import java.util.concurrent.Flow.Publisher; 8 | import java.util.concurrent.Flow.Subscriber; 9 | import java.util.concurrent.Flow.Subscription; 10 | import java.util.concurrent.ForkJoinPool; 11 | import java.util.concurrent.Future; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | import java.util.concurrent.ScheduledFuture; 14 | import java.util.concurrent.SubmissionPublisher; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public class Lesson3 { 18 | 19 | public static void main(String[] args) { 20 | 21 | // Try this: 22 | // SubmissionPublisher weatherForecastPublisher = new OnDemandWeatherForecastPublisher(); 23 | 24 | // Try this: 25 | // SubmissionPublisher weatherForecastPublisher = new SubmissionPublisher<>(); 26 | 27 | SubmissionPublisher weatherForecastPublisher = new WeatherForecastPublisher(); 28 | 29 | weatherForecastPublisher.subscribe(new TwitterSubscriber()); 30 | weatherForecastPublisher.subscribe(new DatabaseSubscriber()); 31 | 32 | // Try this in combination with `weatherForecastPublisher = new SubmissionPiblisher();` 33 | // for (int i = 0; i < Long.MAX_VALUE; i++) { 34 | // weatherForecastPublisher.submit(WeatherForecast.nextRandomWeatherForecast()); 35 | // } 36 | 37 | // close the publisher and associated resources after 10 seconds 38 | try { 39 | TimeUnit.SECONDS.sleep(10); 40 | } catch (InterruptedException e) { 41 | e.printStackTrace(); 42 | } 43 | // Comment out when using OnDemandWeatherForecastPublisher which is not AutoClosable 44 | weatherForecastPublisher.close(); 45 | } 46 | 47 | /** 48 | * Modification of the example from JavaDoc for 49 | * java.util.concurrent.SubmissionPublisher 50 | */ 51 | public static class WeatherForecastPublisher extends SubmissionPublisher { 52 | final ScheduledFuture periodicTask; 53 | final ScheduledExecutorService scheduler; 54 | 55 | WeatherForecastPublisher() { 56 | // Try this: 57 | // super(Executors.newFixedThreadPool(2), Flow.defaultBufferSize()); 58 | scheduler = Executors.newScheduledThreadPool(1); 59 | periodicTask = scheduler.scheduleAtFixedRate( // 60 | // runs submit() 61 | () -> submit(WeatherForecast.nextRandomWeatherForecast()), // 62 | 500, 500, TimeUnit.MILLISECONDS); 63 | } 64 | 65 | public void close() { 66 | periodicTask.cancel(false); 67 | scheduler.shutdown(); 68 | super.close(); 69 | } 70 | } 71 | 72 | /** 73 | * Modifications of OneShotPublisher and OneShotSubscription from the documentation: 74 | * (https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html) 75 | * 76 | */ 77 | public static class OnDemandWeatherForecastPublisher implements Publisher { 78 | private final ExecutorService executor = ForkJoinPool.commonPool(); // daemon-based 79 | 80 | public synchronized void subscribe(Subscriber subscriber) { 81 | subscriber.onSubscribe(new OnDemandWeatherForecastSubscription(subscriber, executor)); 82 | } 83 | } 84 | 85 | static class OnDemandWeatherForecastSubscription implements Subscription { 86 | private final Subscriber subscriber; 87 | private final ExecutorService executor; 88 | private Future future; // to allow cancellation 89 | 90 | OnDemandWeatherForecastSubscription(Subscriber subscriber, 91 | ExecutorService executor) { 92 | this.subscriber = subscriber; 93 | this.executor = executor; 94 | } 95 | 96 | public synchronized void request(long n) { 97 | if (n > 0) { 98 | for (int i = 0; i < n; i++) { 99 | future = executor.submit(() -> { 100 | subscriber.onNext(WeatherForecast.nextRandomWeatherForecast()); 101 | }); 102 | } 103 | } else if (n < 0) { 104 | IllegalArgumentException ex = new IllegalArgumentException(); 105 | executor.execute(() -> subscriber.onError(ex)); 106 | } 107 | } 108 | 109 | public synchronized void cancel() { 110 | if (future != null) 111 | future.cancel(false); 112 | } 113 | } 114 | 115 | private static final class DatabaseSubscriber implements Flow.Subscriber { 116 | private Flow.Subscription subscription; 117 | private final String name = "Database Subscriber"; 118 | 119 | @Override 120 | public void onSubscribe(Subscription subscription) { 121 | System.out.println(name + " subscribed!"); 122 | this.subscription = subscription; 123 | subscription.request(1); 124 | } 125 | 126 | @Override 127 | public void onNext(WeatherForecast weatherForecast) { 128 | System.out.println( 129 | Thread.currentThread().getName() + " > Saving to DB: " + weatherForecast); 130 | subscription.request(1); 131 | } 132 | 133 | @Override 134 | public void onError(Throwable throwable) { 135 | } 136 | 137 | @Override 138 | public void onComplete() { 139 | } 140 | } 141 | 142 | private static final class TwitterSubscriber implements Flow.Subscriber { 143 | private Flow.Subscription subscription; 144 | private final String name = "Twitter Subscriber"; 145 | 146 | @Override 147 | public void onSubscribe(Subscription subscription) { 148 | System.out.println(name + " subscribed!"); 149 | this.subscription = subscription; 150 | subscription.request(1); 151 | } 152 | 153 | @Override 154 | public void onNext(WeatherForecast weatherForecast) { 155 | System.out.println( 156 | Thread.currentThread().getName() + " > Twitting: " + weatherForecast); 157 | subscription.request(1); 158 | } 159 | 160 | @Override 161 | public void onError(Throwable throwable) { 162 | System.err.println(name + " got an error: " + throwable.getMessage()); 163 | } 164 | 165 | @Override 166 | public void onComplete() { 167 | System.out.println(name + " completed."); 168 | } 169 | } 170 | 171 | /** 172 | * Weather Forecast in United States customary units 173 | */ 174 | public static class WeatherForecast { 175 | private final int temperatureInF; 176 | private final int windSpeedInMPH; 177 | private final String weatherCondition; 178 | 179 | private static final Random random = new Random(); 180 | private static final String[] allWeatherConditions = new String[] { "☁️", "☀️", "⛅", "🌧", 181 | "⛈️" }; 182 | 183 | public static WeatherForecast nextRandomWeatherForecast() { 184 | String weatherCondition = allWeatherConditions[random 185 | .nextInt(allWeatherConditions.length)]; 186 | int temperatureInF = random.nextInt(95); 187 | int windSpeedInMPH = 5 + random.nextInt(30); 188 | return new WeatherForecast(temperatureInF, windSpeedInMPH, weatherCondition); 189 | } 190 | 191 | public WeatherForecast(int temperatureInF, int windSpeedInMPH, String weatherCondition) { 192 | super(); 193 | this.temperatureInF = temperatureInF; 194 | this.windSpeedInMPH = windSpeedInMPH; 195 | this.weatherCondition = weatherCondition; 196 | } 197 | 198 | @Override 199 | public String toString() { 200 | return weatherCondition + " " + temperatureInF + "°F wind: " + windSpeedInMPH + "mph"; 201 | } 202 | 203 | @Override 204 | public int hashCode() { 205 | final int prime = 31; 206 | int result = 1; 207 | result = prime * result + temperatureInF; 208 | result = prime * result 209 | + ((weatherCondition == null) ? 0 : weatherCondition.hashCode()); 210 | result = prime * result + windSpeedInMPH; 211 | return result; 212 | } 213 | 214 | @Override 215 | public boolean equals(Object obj) { 216 | if (this == obj) 217 | return true; 218 | if (obj == null) 219 | return false; 220 | if (getClass() != obj.getClass()) 221 | return false; 222 | WeatherForecast other = (WeatherForecast) obj; 223 | if (temperatureInF != other.temperatureInF) 224 | return false; 225 | if (weatherCondition == null) { 226 | if (other.weatherCondition != null) 227 | return false; 228 | } else if (!weatherCondition.equals(other.weatherCondition)) 229 | return false; 230 | if (windSpeedInMPH != other.windSpeedInMPH) 231 | return false; 232 | return true; 233 | } 234 | 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section3/Lesson4.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section3; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.Flow; 5 | import java.util.concurrent.Flow.Subscription; 6 | import java.util.concurrent.ScheduledExecutorService; 7 | import java.util.concurrent.ScheduledFuture; 8 | import java.util.concurrent.ScheduledThreadPoolExecutor; 9 | import java.util.concurrent.SubmissionPublisher; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | public class Lesson4 { 13 | 14 | public static void main(String[] args) { 15 | SubmissionPublisher weatherForecastPublisher = new WeatherForecastPublisher(); 16 | 17 | weatherForecastPublisher.subscribe(new DatabaseSubscriber()); 18 | weatherForecastPublisher.subscribe(new TwitterSubscriber()); 19 | 20 | Flow.Processor metricConverter = new UsToMetricProcessor(); 21 | weatherForecastPublisher.subscribe(metricConverter); 22 | metricConverter.subscribe(new TwitterSubscriber()); 23 | 24 | // close the publisher and associated resources after 10 seconds 25 | try { 26 | TimeUnit.SECONDS.sleep(10); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | weatherForecastPublisher.close(); 31 | } 32 | 33 | public static class UsToMetricProcessor extends SubmissionPublisher 34 | implements Flow.Processor { 35 | 36 | private Flow.Subscription subscription; 37 | 38 | @Override 39 | public void onSubscribe(Flow.Subscription subscription) { 40 | this.subscription = subscription; 41 | subscription.request(1); 42 | } 43 | 44 | @Override 45 | public void onNext(WeatherForecast item) { 46 | submit(MetricWeatherForecast.fromImperial(item)); 47 | subscription.request(1); 48 | } 49 | 50 | @Override 51 | public void onError(Throwable e) { 52 | e.printStackTrace(); 53 | } 54 | 55 | @Override 56 | public void onComplete() { 57 | close(); 58 | } 59 | } 60 | 61 | public static class MetricWeatherForecast { 62 | 63 | public static MetricWeatherForecast fromImperial(WeatherForecast imperial) { 64 | int windSpeedInKMH = (int) (imperial.windSpeedInMPH * 1.60934); 65 | int temperatureInC = (int) ((imperial.temperatureInF - 32) / 1.8); 66 | return new MetricWeatherForecast(temperatureInC, windSpeedInKMH, 67 | imperial.weatherCondition); 68 | } 69 | 70 | private final int temperatureInC; 71 | private final int windSpeedInKMH; 72 | private final String weatherCondition; 73 | 74 | private static final Random random = new Random(); 75 | private static final String[] allWeatherConditions = new String[] { "☁️", "☀️", "⛅", "🌧", 76 | "⛈️" }; 77 | 78 | public static WeatherForecast nextRandomWeatherForecast() { 79 | String weatherCondition = allWeatherConditions[random 80 | .nextInt(allWeatherConditions.length)]; 81 | int temperatureInF = random.nextInt(95); 82 | int windSpeedInMPH = 5 + random.nextInt(30); 83 | return new WeatherForecast(temperatureInF, windSpeedInMPH, weatherCondition); 84 | } 85 | 86 | public MetricWeatherForecast(int temperatureInC, int windSpeedInKMH, 87 | String weatherCondition) { 88 | super(); 89 | this.temperatureInC = temperatureInC; 90 | this.windSpeedInKMH = windSpeedInKMH; 91 | this.weatherCondition = weatherCondition; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return weatherCondition + " " + temperatureInC + "°C wind: " + windSpeedInKMH + "km/h"; 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | final int prime = 31; 102 | int result = 1; 103 | result = prime * result + temperatureInC; 104 | result = prime * result 105 | + ((weatherCondition == null) ? 0 : weatherCondition.hashCode()); 106 | result = prime * result + windSpeedInKMH; 107 | return result; 108 | } 109 | 110 | @Override 111 | public boolean equals(Object obj) { 112 | if (this == obj) 113 | return true; 114 | if (obj == null) 115 | return false; 116 | if (getClass() != obj.getClass()) 117 | return false; 118 | MetricWeatherForecast other = (MetricWeatherForecast) obj; 119 | if (temperatureInC != other.temperatureInC) 120 | return false; 121 | if (weatherCondition == null) { 122 | if (other.weatherCondition != null) 123 | return false; 124 | } else if (!weatherCondition.equals(other.weatherCondition)) 125 | return false; 126 | if (windSpeedInKMH != other.windSpeedInKMH) 127 | return false; 128 | return true; 129 | } 130 | 131 | } 132 | 133 | private static final class DatabaseSubscriber implements Flow.Subscriber { 134 | private Flow.Subscription subscription; 135 | private final String name = "Database Subscriber"; 136 | 137 | @Override 138 | public void onSubscribe(Subscription subscription) { 139 | System.out.println(name + " subscribed!"); 140 | this.subscription = subscription; 141 | subscription.request(1); 142 | } 143 | 144 | @Override 145 | public void onNext(WeatherForecast weatherForecast) { 146 | System.out.println( 147 | Thread.currentThread().getName() + " > Saving to DB: " + weatherForecast); 148 | subscription.request(1); 149 | } 150 | 151 | @Override 152 | public void onError(Throwable throwable) { 153 | } 154 | 155 | @Override 156 | public void onComplete() { 157 | } 158 | } 159 | 160 | private static final class TwitterSubscriber implements Flow.Subscriber { 161 | private Flow.Subscription subscription; 162 | private final String name = "Twitter Subscriber"; 163 | 164 | @Override 165 | public void onSubscribe(Subscription subscription) { 166 | System.out.println(name + " subscribed!"); 167 | this.subscription = subscription; 168 | subscription.request(1); 169 | } 170 | 171 | @Override 172 | public void onNext(T weatherForecast) { 173 | System.out 174 | .println(Thread.currentThread().getName() + " > Twitting: " + weatherForecast); 175 | // You can alternatively use subscription.request(Long.MAX_VALUE) in 176 | // onSubscribe() 177 | subscription.request(1); 178 | } 179 | 180 | @Override 181 | public void onError(Throwable throwable) { 182 | System.err.println(name + " got an error: " + throwable.getMessage()); 183 | } 184 | 185 | @Override 186 | public void onComplete() { 187 | System.out.println(name + " completed."); 188 | } 189 | } 190 | 191 | /** 192 | * Modification of the example from JavaDoc for 193 | * java.util.concurrent.SubmissionPublisher 194 | */ 195 | public static class WeatherForecastPublisher extends SubmissionPublisher { 196 | final ScheduledFuture periodicTask; 197 | final ScheduledExecutorService scheduler; 198 | 199 | WeatherForecastPublisher() { 200 | scheduler = new ScheduledThreadPoolExecutor(1); 201 | periodicTask = scheduler.scheduleAtFixedRate( // 202 | // runs submit() 203 | () -> submit(WeatherForecast.nextRandomWeatherForecast()), // 204 | 500, 500, TimeUnit.MILLISECONDS); 205 | } 206 | 207 | public void close() { 208 | periodicTask.cancel(false); 209 | scheduler.shutdown(); 210 | super.close(); 211 | } 212 | } 213 | 214 | /** 215 | * Weather Forecast in United States customary units 216 | */ 217 | public static class WeatherForecast { 218 | private final int temperatureInF; 219 | private final int windSpeedInMPH; 220 | private final String weatherCondition; 221 | 222 | private static final Random random = new Random(); 223 | private static final String[] allWeatherConditions = new String[] { "☁️", "☀️", "⛅", "🌧", 224 | "⛈️" }; 225 | 226 | public static WeatherForecast nextRandomWeatherForecast() { 227 | String weatherCondition = allWeatherConditions[random 228 | .nextInt(allWeatherConditions.length)]; 229 | int temperatureInF = random.nextInt(95); 230 | int windSpeedInMPH = 5 + random.nextInt(30); 231 | return new WeatherForecast(temperatureInF, windSpeedInMPH, weatherCondition); 232 | } 233 | 234 | public WeatherForecast(int temperatureInF, int windSpeedInMPH, String weatherCondition) { 235 | super(); 236 | this.temperatureInF = temperatureInF; 237 | this.windSpeedInMPH = windSpeedInMPH; 238 | this.weatherCondition = weatherCondition; 239 | } 240 | 241 | @Override 242 | public String toString() { 243 | return weatherCondition + " " + temperatureInF + "°F wind: " + windSpeedInMPH + "mph"; 244 | } 245 | 246 | @Override 247 | public int hashCode() { 248 | final int prime = 31; 249 | int result = 1; 250 | result = prime * result + temperatureInF; 251 | result = prime * result 252 | + ((weatherCondition == null) ? 0 : weatherCondition.hashCode()); 253 | result = prime * result + windSpeedInMPH; 254 | return result; 255 | } 256 | 257 | @Override 258 | public boolean equals(Object obj) { 259 | if (this == obj) 260 | return true; 261 | if (obj == null) 262 | return false; 263 | if (getClass() != obj.getClass()) 264 | return false; 265 | WeatherForecast other = (WeatherForecast) obj; 266 | if (temperatureInF != other.temperatureInF) 267 | return false; 268 | if (weatherCondition == null) { 269 | if (other.weatherCondition != null) 270 | return false; 271 | } else if (!weatherCondition.equals(other.weatherCondition)) 272 | return false; 273 | if (windSpeedInMPH != other.windSpeedInMPH) 274 | return false; 275 | return true; 276 | } 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section4/Lesson1.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section4; 2 | 3 | public class Lesson1 { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | demoThreadState(); 7 | } 8 | 9 | private static void demoThreadState() throws InterruptedException { 10 | System.out.println("Main thread: " + Thread.currentThread().getState()); 11 | System.out.println(); 12 | 13 | Runnable sayHello = () -> { 14 | System.out.println(" Hi there!"); 15 | }; 16 | Thread thread = new Thread(sayHello); 17 | 18 | // nothing happens until the thread starts 19 | System.out.println("After creation: " + thread.getState()); 20 | 21 | thread.start(); 22 | System.out.println("After thread.start(): " + thread.getState()); 23 | 24 | // Wait until the second thread completes its execution either by sleeping or by 25 | // joining 26 | thread.join(); 27 | // or 28 | try { 29 | Thread.sleep(500, 0); // == TimeUnit.MILLISECONDS.sleep(1000) 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | 34 | System.out.println("When completed execution: " + thread.getState()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section4/Lesson2.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section4; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class Lesson2 { 8 | 9 | public static void main(String[] args) throws InterruptedException { 10 | demoSynchronized(); 11 | demoBlockedState(); 12 | } 13 | 14 | public static void demoSynchronized() throws InterruptedException { 15 | int numberOfWorkers = 2; 16 | ExecutorService service = Executors.newFixedThreadPool(numberOfWorkers); 17 | 18 | Counter counter = new Counter(); 19 | for (int i = 0; i < 10_000; i++) { 20 | service.submit(() -> counter.increment()); 21 | } 22 | 23 | service.awaitTermination(1000, TimeUnit.MILLISECONDS); 24 | 25 | System.out.println("Increment 10_000 times: " + counter.getValue()); 26 | } 27 | 28 | public static class Counter { 29 | private final Object lock = new Object(); 30 | 31 | private int value = 0; 32 | 33 | public synchronized void increment() { 34 | // Try this after removing "synchronized void" 35 | // synchronized (this) { 36 | // Also try this: 37 | // synchronized (lock) { 38 | value++; 39 | // } 40 | } 41 | 42 | public synchronized void decrement() { 43 | value--; 44 | } 45 | 46 | public synchronized void synchronizedAddOne() { 47 | value++; 48 | } 49 | 50 | public static void staticMethodCanBeSynchronizedToo() { 51 | synchronized (Counter.class) { 52 | System.out.println("A static method can be synchronized too"); 53 | } 54 | } 55 | 56 | public static synchronized void staticMethodCanBeSynchronizedToo2() { 57 | System.out.println("A static method can be synchronized too"); 58 | } 59 | 60 | public synchronized int getValue() { 61 | return value; 62 | } 63 | } 64 | 65 | public static void demoBlockedState() { 66 | System.out.println(); 67 | final Object lock = new Object(); 68 | 69 | Runnable waitALittle = () -> { 70 | synchronized (lock) { 71 | System.out.println("Wait a little..."); 72 | while (true) { 73 | // it will take forever! 74 | // let's block everything!! 75 | } 76 | } 77 | }; 78 | 79 | Thread iAmBusy = new Thread(waitALittle); 80 | Thread iAmBusyToo = new Thread(waitALittle); 81 | 82 | iAmBusy.start(); 83 | iAmBusyToo.start(); 84 | 85 | try { 86 | TimeUnit.MILLISECONDS.sleep(50); 87 | } catch (InterruptedException e) { 88 | e.printStackTrace(); 89 | } 90 | 91 | System.out.println("First thread: " + iAmBusy.getState()); 92 | System.out.println("Second thread: " + iAmBusyToo.getState()); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section4/Lesson3.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section4; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.locks.Lock; 7 | import java.util.concurrent.locks.ReadWriteLock; 8 | import java.util.concurrent.locks.ReentrantReadWriteLock; 9 | 10 | public class Lesson3 { 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | demoLocks(); 14 | } 15 | 16 | public static void demoLocks() throws InterruptedException { 17 | int numberOfWorkers = 2; 18 | ExecutorService service = Executors.newFixedThreadPool(numberOfWorkers); 19 | 20 | Counter counter = new Counter(); 21 | for (int i = 0; i < 10_000; i++) { 22 | service.submit(() -> counter.increment()); 23 | } 24 | 25 | service.awaitTermination(1000, TimeUnit.MILLISECONDS); 26 | 27 | System.out.println("Increment 10_000 times: " + counter.getValue()); 28 | } 29 | 30 | public static class Counter { 31 | // Try this: 32 | // private final Lock lock = new ReentrantLock(); 33 | 34 | private final ReadWriteLock lock = new ReentrantReadWriteLock(); 35 | private final Lock readLock = lock.readLock(); 36 | private final Lock writeLock = lock.writeLock(); 37 | 38 | private int value = 0; 39 | 40 | public void increment() { 41 | writeLock.lock(); 42 | try { 43 | value++; 44 | } finally { 45 | // make sure to unlock!!!!! 46 | writeLock.unlock(); 47 | } 48 | } 49 | 50 | public void decrement() { 51 | writeLock.lock(); 52 | try { 53 | value--; 54 | } finally { 55 | // make sure to unlock!!!!! 56 | writeLock.unlock(); 57 | } 58 | } 59 | 60 | /** 61 | * tryLock() 62 | */ 63 | public void tryIncrement() { 64 | if (writeLock.tryLock()) { 65 | try { 66 | value++; 67 | } finally { 68 | // make sure to unlock!!!!! 69 | writeLock.unlock(); 70 | } 71 | } else { 72 | System.out.println("Whatever... I tried"); 73 | } 74 | } 75 | 76 | public int getValue() { 77 | readLock.lock(); 78 | try { 79 | return value; 80 | } finally { 81 | readLock.unlock(); 82 | } 83 | } 84 | 85 | public String getBinaryValue() { 86 | readLock.lock(); 87 | try { 88 | return Integer.toBinaryString(value); 89 | } finally { 90 | readLock.unlock(); 91 | } 92 | } 93 | 94 | } 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section4/Lesson4.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section4; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | import java.util.concurrent.locks.Condition; 10 | import java.util.concurrent.locks.Lock; 11 | import java.util.concurrent.locks.ReentrantLock; 12 | 13 | public class Lesson4 { 14 | 15 | public static void main(String[] args) throws InterruptedException { 16 | demoWaitForGreenLight(); 17 | demoWaitNotifyWithMessageQueue(); 18 | } 19 | 20 | private static void demoWaitNotifyWithMessageQueue() throws InterruptedException { 21 | ExecutorService service = Executors.newFixedThreadPool(2); 22 | // Try MessageQueueWithWaitNotify messageQueue = new MessageQueueWithWaitNotify(); 23 | 24 | // Try MessageQueueWithLockConditions messageQueue = new MessageQueueWithLockConditions(); 25 | 26 | BrokenMessageQueue messageQueue = new BrokenMessageQueue(); 27 | 28 | Runnable producer = () -> { 29 | // American journalist Robert Benchley sent this message from Venice 30 | // “Streets full of water. Please advise.” 31 | String[] messages = { "Streets", " full of water.", " Please", "advise." }; 32 | for (String message : messages) { 33 | System.out.format("%s sending >> %s%n", Thread.currentThread().getName(), message); 34 | messageQueue.send(message); 35 | try { 36 | TimeUnit.MILLISECONDS.sleep(200); 37 | } catch (InterruptedException e) { 38 | } 39 | } 40 | }; 41 | 42 | Runnable consumer = () -> { 43 | for (int i = 0; i < 4; i++) { 44 | String message = messageQueue.receive(); 45 | System.out.format("%s received << %s%n", Thread.currentThread().getName(), message); 46 | try { 47 | TimeUnit.MILLISECONDS.sleep(0); 48 | } catch (InterruptedException e) { 49 | } 50 | } 51 | }; 52 | 53 | service.submit(producer); 54 | service.submit(consumer); 55 | 56 | service.awaitTermination(5000, TimeUnit.MILLISECONDS); 57 | 58 | } 59 | 60 | private static class BrokenMessageQueue { 61 | 62 | private final int capacity = 2; 63 | 64 | private final Queue queue = new LinkedList<>(); 65 | 66 | // not synchronized 67 | public void send(String message) { 68 | while (queue.size() == capacity) { 69 | // wait until queue is able to accept new elements, not full 70 | } 71 | queue.add(message); 72 | // A new element added to the queue!!! 73 | } 74 | 75 | public String receive() { 76 | while (queue.size() == 0) { 77 | // wait until queue has elements, not empty 78 | } 79 | String value = queue.poll(); 80 | // An element removed from a queue!!! 81 | return value; 82 | } 83 | 84 | } 85 | 86 | private static class MessageQueueWithWaitNotify { 87 | 88 | private final int capacity = 2; 89 | 90 | private final Queue queue = new LinkedList<>(); 91 | 92 | public synchronized void send(String message) { 93 | while (queue.size() == capacity) { 94 | // wait until queue is not full 95 | try { 96 | wait(); 97 | } catch (InterruptedException e) { 98 | } 99 | } 100 | queue.add(message); 101 | notifyAll(); 102 | } 103 | 104 | public synchronized String receive() { 105 | while (queue.size() == 0) { 106 | try { 107 | wait(); 108 | } catch (InterruptedException e) { 109 | } 110 | } 111 | String value = queue.poll(); 112 | notifyAll(); 113 | return value; 114 | } 115 | 116 | } 117 | 118 | private static class MessageQueueWithLockConditions { 119 | 120 | private final int capacity = 2; 121 | 122 | private final Lock lock = new ReentrantLock(); 123 | 124 | /** true if ready to send/produce */ 125 | private final Condition queueNotFull = lock.newCondition(); 126 | 127 | /** true if ready to receive/consume */ 128 | private final Condition queueNotEmpty = lock.newCondition(); 129 | 130 | private final Queue queue = new LinkedList<>(); 131 | 132 | public void send(String message) { 133 | lock.lock(); 134 | try { 135 | while (queue.size() == capacity) { 136 | try { 137 | queueNotFull.await(); 138 | } catch (InterruptedException e) { 139 | } 140 | } 141 | queue.add(message); 142 | queueNotEmpty.signalAll(); 143 | } finally { 144 | lock.unlock(); 145 | } 146 | } 147 | 148 | public String receive() { 149 | lock.lock(); 150 | try { 151 | while (queue.size() == 0) { 152 | try { 153 | queueNotEmpty.await(); 154 | } catch (InterruptedException e) { 155 | } 156 | } 157 | String value = queue.poll(); 158 | queueNotFull.signalAll(); 159 | return value; 160 | } finally { 161 | lock.unlock(); 162 | } 163 | } 164 | 165 | } 166 | 167 | private static void demoWaitForGreenLight() throws InterruptedException { 168 | demoOnSpinWait(); 169 | demoWaitNotify(); 170 | } 171 | 172 | private static void demoOnSpinWait() throws InterruptedException { 173 | final AtomicBoolean isGreenLight = new AtomicBoolean(false); 174 | 175 | Runnable waitForGreenLightAndGo = () -> { 176 | System.out.println("Waiting for the green light..."); 177 | while (!isGreenLight.get()) { 178 | Thread.onSpinWait(); 179 | } 180 | System.out.println("Go!!!"); 181 | }; 182 | new Thread(waitForGreenLightAndGo).start(); 183 | 184 | TimeUnit.MILLISECONDS.sleep(3000); 185 | 186 | // from the main thread: 187 | isGreenLight.set(true); 188 | } 189 | 190 | public static void demoWaitNotify() throws InterruptedException { 191 | final AtomicBoolean isGreenLight = new AtomicBoolean(false); 192 | 193 | Object lock = new Object(); 194 | 195 | Runnable waitForGreenLightAndGo = () -> { 196 | System.out.println("Waiting for the green light..."); 197 | synchronized (lock) { 198 | while (!isGreenLight.get()) { 199 | try { 200 | lock.wait(); 201 | } catch (InterruptedException e) { 202 | } 203 | } 204 | } 205 | System.out.println("Go!!!"); 206 | }; 207 | new Thread(waitForGreenLightAndGo).start(); 208 | 209 | TimeUnit.MILLISECONDS.sleep(3000); 210 | 211 | // from the main thread: 212 | synchronized (lock) { 213 | isGreenLight.set(true); 214 | lock.notify(); 215 | } 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section5/Lesson1.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section5; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | import com.packt.tfesenko.multithreading.section2.Lesson2; 9 | import com.packt.tfesenko.multithreading.section4.Lesson4; 10 | 11 | public class Lesson1 { 12 | 13 | public static void main(String[] args) throws InterruptedException { 14 | demoAtomicInteger(); 15 | 16 | // Atomic boolean in section 4 17 | Lesson4.demoWaitNotify(); 18 | 19 | // Atomic integer usage in section 2 20 | Lesson2.demoThreadFactory(); 21 | } 22 | 23 | // Modification of 24 | // com.packt.tfesenko.multithreading.section4.Lesson2.demoSynchronized() 25 | public static void demoAtomicInteger() throws InterruptedException { 26 | int numberOfWorkers = 2; 27 | ExecutorService service = Executors.newFixedThreadPool(numberOfWorkers); 28 | 29 | Counter counter = new Counter(); 30 | for (int i = 0; i < 10_000; i++) { 31 | service.submit(() -> counter.increment()); 32 | } 33 | 34 | service.awaitTermination(1000, TimeUnit.MILLISECONDS); 35 | 36 | System.out.println("Increment 10_000 times: " + counter.getValue()); 37 | } 38 | 39 | public static class Counter { 40 | private AtomicInteger value = new AtomicInteger();// 0 is default 41 | 42 | public void increment() { 43 | value.incrementAndGet(); 44 | // this also works: value.addAndGet(1); 45 | } 46 | 47 | public void decrement() { 48 | value.decrementAndGet(); 49 | } 50 | 51 | public int getValue() { 52 | return value.get(); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section5/Lesson2.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section5; 2 | 3 | import java.util.Map; 4 | import java.util.Random; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.ConcurrentLinkedQueue; 7 | import java.util.concurrent.ConcurrentMap; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.function.BiConsumer; 12 | import java.util.function.Consumer; 13 | 14 | public class Lesson2 { 15 | 16 | public static void main(String[] args) throws InterruptedException { 17 | // demoConcurrentHashMap(); 18 | demoConcurrentLinkedQueue(); 19 | 20 | } 21 | 22 | private static void demoConcurrentHashMap() throws InterruptedException { 23 | Random random = new Random(); 24 | ExecutorService service = Executors.newCachedThreadPool(); 25 | 26 | String brandNewShoes = "Brand new shows"; 27 | String oldPhone = "Old phone"; 28 | String leatherHat = "Leather hat"; 29 | String cowboyShoes = "Cowboy shoes"; 30 | 31 | ConcurrentMap itemToBuyerMap = new ConcurrentHashMap<>(); 32 | 33 | BiConsumer buyItemIfNotTaken = (buyer, item) -> { 34 | try { 35 | TimeUnit.MILLISECONDS.sleep(random.nextInt(1000)); 36 | itemToBuyerMap.putIfAbsent(item, buyer); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | }; 41 | 42 | service.submit(() -> { 43 | buyItemIfNotTaken.accept("Alice", brandNewShoes); 44 | buyItemIfNotTaken.accept("Alice", cowboyShoes); 45 | buyItemIfNotTaken.accept("Alice", leatherHat); 46 | }); 47 | 48 | service.submit(() -> { 49 | buyItemIfNotTaken.accept("Bob", brandNewShoes); 50 | buyItemIfNotTaken.accept("Bob", cowboyShoes); 51 | buyItemIfNotTaken.accept("Bob", leatherHat); 52 | }); 53 | 54 | service.submit(() -> { 55 | buyItemIfNotTaken.accept("Carol", brandNewShoes); 56 | buyItemIfNotTaken.accept("Carol", cowboyShoes); 57 | buyItemIfNotTaken.accept("Carol", leatherHat); 58 | }); 59 | 60 | service.awaitTermination(2000, TimeUnit.MILLISECONDS); 61 | 62 | itemToBuyerMap 63 | .forEach((item, buyer) -> System.out.printf("%s bought by %s%n", item, buyer)); 64 | } 65 | 66 | private static void demoConcurrentLinkedQueue() throws InterruptedException { 67 | Random random = new Random(); 68 | ExecutorService service = Executors.newCachedThreadPool(); 69 | 70 | ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); 71 | Consumer joinQueue = (name) -> { 72 | try { 73 | TimeUnit.MILLISECONDS.sleep(random.nextInt(1000)); 74 | queue.offer(name); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | }; 79 | 80 | service.submit(() -> joinQueue.accept("Alice")); 81 | service.submit(() -> joinQueue.accept("Bob")); 82 | service.submit(() -> joinQueue.accept("Carol")); 83 | service.submit(() -> joinQueue.accept("Daniel")); 84 | 85 | // Try null: 86 | // service.submit(() -> joinQueue.accept(null)); 87 | 88 | // wait so at least several elements are in the queue 89 | try { 90 | TimeUnit.MILLISECONDS.sleep(500); 91 | } catch (InterruptedException e) { 92 | } 93 | 94 | service.submit(() -> System.out.println("poll(): " + queue.poll())); 95 | service.submit(() -> System.out.println("poll(): " + queue.poll())); 96 | service.submit(() -> System.out.println("poll(): " + queue.poll())); 97 | service.submit(() -> System.out.println("poll(): " + queue.poll())); 98 | service.submit(() -> System.out.println("poll(): " + queue.poll())); 99 | 100 | service.awaitTermination(2000, TimeUnit.MILLISECONDS); 101 | 102 | System.out.println("\n Remaing elements: "); 103 | queue.forEach((name) -> System.out.println(name)); 104 | 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section5/Lesson3.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section5; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.Random; 6 | import java.util.concurrent.BlockingQueue; 7 | import java.util.concurrent.ConcurrentLinkedQueue; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.function.Consumer; 13 | import java.util.function.Function; 14 | 15 | public class Lesson3 { 16 | 17 | public static void main(String[] args) throws InterruptedException { 18 | // demoBlockingQueue(); 19 | demoMessageQueue(); 20 | } 21 | 22 | // Modification of 23 | // com.packt.tfesenko.multithreading.section5.Lesson2.demoConcurrentLinkedQueue() 24 | private static void demoBlockingQueue() throws InterruptedException { 25 | Random random = new Random(); 26 | ExecutorService service = Executors.newCachedThreadPool(); 27 | 28 | BlockingQueue queue = new LinkedBlockingQueue<>(); 29 | Consumer joinQueue = (name) -> { 30 | try { 31 | TimeUnit.MILLISECONDS.sleep(random.nextInt(1000)); 32 | queue.offer(name); 33 | } catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | }; 37 | 38 | service.submit(() -> joinQueue.accept("Alice")); 39 | service.submit(() -> joinQueue.accept("Bob")); 40 | service.submit(() -> joinQueue.accept("Carol")); 41 | service.submit(() -> joinQueue.accept("Daniel")); 42 | 43 | Runnable dequeue = () -> { 44 | try { 45 | System.out.println("poll(): " + queue.take()); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | } 49 | }; 50 | 51 | service.submit(dequeue); 52 | service.submit(dequeue); 53 | service.submit(dequeue); 54 | service.submit(dequeue); 55 | 56 | service.awaitTermination(2000, TimeUnit.MILLISECONDS); 57 | 58 | System.out.println("\n Remaing elements: "); 59 | queue.forEach((name) -> System.out.println(name)); 60 | 61 | } 62 | 63 | // Modified 64 | // com.packt.tfesenko.multithreading.section4.Lesson4.demoWaitNotifyWithMessageQueue() 65 | private static void demoMessageQueue() throws InterruptedException { 66 | ExecutorService service = Executors.newFixedThreadPool(2); 67 | MessageQueue messageQueue = new MessageQueue(); 68 | 69 | Runnable producer = () -> { 70 | // American journalist Robert Benchley sent this message from Venice 71 | // “Streets full of water. Please advise.” 72 | String[] messages = { "Streets", " full of water.", " Please", "advise." }; 73 | for (String message : messages) { 74 | System.out.format("%s sending >> %s%n", Thread.currentThread().getName(), message); 75 | messageQueue.send(message); 76 | try { 77 | TimeUnit.MILLISECONDS.sleep(1000); 78 | } catch (InterruptedException e) { 79 | } 80 | } 81 | }; 82 | 83 | Runnable consumer = () -> { 84 | for (int i = 0; i < 4; i++) { 85 | try { 86 | String message = messageQueue.receive(); 87 | System.out.format("%s received << %s%n", Thread.currentThread().getName(), 88 | message); 89 | TimeUnit.MILLISECONDS.sleep(0); 90 | } catch (InterruptedException e) { 91 | } 92 | } 93 | }; 94 | 95 | service.submit(producer); 96 | service.submit(consumer); 97 | 98 | service.awaitTermination(5000, TimeUnit.MILLISECONDS); 99 | 100 | } 101 | 102 | // MessageQueue implemented with BlockingQueue 103 | private static class MessageQueue { 104 | 105 | private final LinkedBlockingQueue queue = new LinkedBlockingQueue<>(2); 106 | 107 | public void send(String message) { 108 | queue.add(message); 109 | } 110 | 111 | public String receive() throws InterruptedException { 112 | String value = queue.take(); 113 | return value; 114 | } 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/packt/tfesenko/multithreading/section5/Lesson4.java: -------------------------------------------------------------------------------- 1 | package com.packt.tfesenko.multithreading.section5; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.CopyOnWriteArrayList; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | public class Lesson4 { 12 | 13 | public static void main(String[] args) throws InterruptedException { 14 | ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4); 15 | 16 | // For Java 8, use Arrays.asList(...) 17 | List initialElements = List.of("Ella", "Eclair", "Larry", "Felix"); 18 | 19 | List cats = new CopyOnWriteArrayList<>(initialElements); 20 | 21 | Runnable feedCats = () -> { 22 | try { 23 | for (String cat : cats) { 24 | System.out.println("Feeding " + cat); 25 | } 26 | System.out.println(); 27 | 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | }; 32 | 33 | scheduler.scheduleAtFixedRate(feedCats, 0, 100, TimeUnit.MILLISECONDS); 34 | 35 | AtomicInteger communityCatNumber = new AtomicInteger(1); 36 | // This will eventually cause a ConcurrentModificationException 37 | Runnable adoptCommunityCat = () -> { 38 | try { 39 | cats.add("Community cat " + communityCatNumber.getAndIncrement()); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | }; 44 | scheduler.scheduleAtFixedRate(adoptCommunityCat, 1, 1000, TimeUnit.MILLISECONDS); 45 | 46 | TimeUnit.SECONDS.sleep(20); 47 | scheduler.shutdown(); 48 | 49 | } 50 | 51 | } 52 | --------------------------------------------------------------------------------