├── .gitignore ├── README.md ├── assets ├── java-concurrency │ ├── blockingqueue │ │ └── blockingqueue-1.png │ ├── compare-and-swap │ │ ├── compare-and-swap-0.png │ │ └── compare-and-swap-1.png │ ├── deadlock-prevention │ │ └── deadlock-detection.png │ ├── false-sharing │ │ └── false-sharing.png │ ├── forkjoinpool │ │ ├── fork-join-pool-1.png │ │ ├── fork-join-pool-2.png │ │ ├── fork-join-pool-3.png │ │ └── fork-join-pool-4.png │ ├── producer-consumer │ │ ├── producer-consumer-overview.png │ │ ├── producer-consumer-use-cases-1.png │ │ └── producer-consumer-use-cases-2.png │ ├── thread-congestion │ │ ├── thread-congestion-1.png │ │ ├── thread-congestion-2.png │ │ ├── thread-congestion-3.png │ │ └── thread-congestion-4.png │ ├── tread-signaling │ │ ├── thread-signaling-1.png │ │ └── thread-signaling-2.png │ └── virtual-threads │ │ └── virtual-threads-1.png └── toc.txt └── src └── main └── java └── com └── jenkov └── java ├── collections └── SetOfExample.java ├── concurrency ├── blockingqueue │ ├── AdditionalMethods.java │ ├── BlockingQueueExample.java │ ├── BlockingQueueUsageExample.java │ ├── Consumer.java │ ├── DequeueMethods.java │ ├── DrainMethods.java │ ├── EnqueueMethods.java │ ├── PeekMethods.java │ ├── Producer.java │ └── ProducerConsumerExample.java ├── compareandswap │ ├── CompareAndSwapExample.java │ ├── CompareAndSwapLock.java │ ├── CountRunnable.java │ ├── Counter.java │ ├── MyLock.java │ ├── OptimisticLockCounter.java │ ├── ProblematicLock.java │ └── SimpleCounter.java ├── deadlock │ ├── DeadlockExample.java │ ├── Runnable1.java │ ├── Runnable2.java │ ├── RunnableSync1.java │ ├── RunnableSync2.java │ ├── detection │ │ ├── DeadlockDetection.java │ │ ├── DeadlockDetectionExample.java │ │ ├── LockGraphFacade.java │ │ ├── LockGraphFacadeExample.java │ │ ├── LockNode.java │ │ ├── Runnable1DeadlockDetection.java │ │ ├── Runnable2DeadlockDetection.java │ │ └── ThreadNode.java │ └── prevention │ │ ├── DeadlockTimeoutExample.java │ │ ├── Runnable1TimeOut.java │ │ └── Runnable2TimeOut.java ├── falsesharing │ ├── Counter1.java │ ├── Counter2.java │ ├── Counter3.java │ ├── Counter4.java │ └── FalseSharingExample.java ├── forkjoinpool │ ├── JavaForkJoinPool2Example.java │ ├── JavaForkJoinPoolExample.java │ ├── MyRecursiveAction.java │ └── MyRecursiveTask.java ├── samethreading │ ├── BackgroundRunnable.java │ ├── ForegroundRunnable.java │ ├── IAgent.java │ ├── Main.java │ ├── PrintAgent.java │ ├── SubAgent.java │ ├── SystemThreadRunnable.java │ └── ThreadSystem.java ├── threadcongestion │ ├── ConsumerRunnable.java │ ├── ThreadCongestionExample.java │ ├── ThreadCongestionExample2.java │ └── ThreadPipelineMain.java ├── threadpipeline │ ├── Processor.java │ ├── Step1Processor.java │ ├── Step2Processor.java │ └── ThreadPipeline.java ├── threadsignaling │ ├── SignalCarrier.java │ ├── SignalCounter.java │ ├── SignalHolder.java │ ├── SpuriousWakeupSignalGuard.java │ ├── ThreadSignalingExample.java │ ├── ThreadSignalingExample2.java │ ├── ThreadSignalingExample3.java │ ├── ThreadSignalingExample4.java │ └── ThreadSignalingExampleBasic.java └── virtualthreads │ ├── ExecutorServiceVirtualThreadsExample.java │ ├── VirtualThreadExample.java │ └── VirtualThreadExample2.java ├── generics ├── Car.java ├── JavaGenericsExample1.java ├── JavaGenericsExample2.java ├── Truck.java └── Vehicle.java └── switchexp └── SwitchExamples.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | 4 | target/ 5 | out/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Examples 2 | A set of Java examples - of Java SE features (core Java) and techniques. 3 | 4 | The Java examples are related to the Java tutorials at jenkov.com . 5 | Many of these Java examples will be hard to understand without the corresponding tutorial explaining the topic and 6 | the examples. 7 | 8 | # Examples Use Latest Version of Java 9 | Since part of the examples in this Git repository are using new Java language features - you should use the 10 | latest version of Java when trying to run them. 11 | 12 | ## Java Language Examples 13 | - [Java Generics Example](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/virtualthreads/VirtualThreadExample.java) 14 | 15 | ## Java Concurrency Examples 16 | 17 | - [Java Virtual Thread Example](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/virtualthreads/VirtualThreadExample.java) 18 | - [Java Deadlock](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/deadlock/DeadlockExample.java) 19 | - [Java Deadlock Prevention - Timeout Backoff](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/deadlock/prevention/DeadlockTimeoutExample.java) 20 | - [Java False Sharing Example](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/falsesharing/FalseSharingExample.java) 21 | - [Java Thread Congestion - Shared BlockingQueue Example](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadcongestion/ThreadCongestionExample.java) 22 | - [Java Thread Congestion - Separate BlockingQueue Example](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadcongestion/ThreadCongestionExample2.java) 23 | - [Java Thread Signaling Example 1](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample.java) 24 | - [Java Thread Signaling Example 2](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample2.java) 25 | - [Java Thread Signaling Example 3](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample3.java) 26 | - [Java Thread Signaling Example 4](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample4.java) 27 | - [Java ForkJoinPool Example 1](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/forkjoinpool/JavaForkJoinPoolExample.java) 28 | - [Java ForkJoinPool Example 2](https://github.com/jjenkov/java-examples/blob/main/src/main/java/com/jenkov/java/concurrency/forkjoinpool/JavaForkJoinPoolExample2.java) 29 | 30 | 31 | -------------------------------------------------------------------------------- /assets/java-concurrency/blockingqueue/blockingqueue-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/blockingqueue/blockingqueue-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/compare-and-swap/compare-and-swap-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/compare-and-swap/compare-and-swap-0.png -------------------------------------------------------------------------------- /assets/java-concurrency/compare-and-swap/compare-and-swap-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/compare-and-swap/compare-and-swap-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/deadlock-prevention/deadlock-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/deadlock-prevention/deadlock-detection.png -------------------------------------------------------------------------------- /assets/java-concurrency/false-sharing/false-sharing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/false-sharing/false-sharing.png -------------------------------------------------------------------------------- /assets/java-concurrency/forkjoinpool/fork-join-pool-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/forkjoinpool/fork-join-pool-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/forkjoinpool/fork-join-pool-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/forkjoinpool/fork-join-pool-2.png -------------------------------------------------------------------------------- /assets/java-concurrency/forkjoinpool/fork-join-pool-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/forkjoinpool/fork-join-pool-3.png -------------------------------------------------------------------------------- /assets/java-concurrency/forkjoinpool/fork-join-pool-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/forkjoinpool/fork-join-pool-4.png -------------------------------------------------------------------------------- /assets/java-concurrency/producer-consumer/producer-consumer-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/producer-consumer/producer-consumer-overview.png -------------------------------------------------------------------------------- /assets/java-concurrency/producer-consumer/producer-consumer-use-cases-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/producer-consumer/producer-consumer-use-cases-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/producer-consumer/producer-consumer-use-cases-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/producer-consumer/producer-consumer-use-cases-2.png -------------------------------------------------------------------------------- /assets/java-concurrency/thread-congestion/thread-congestion-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/thread-congestion/thread-congestion-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/thread-congestion/thread-congestion-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/thread-congestion/thread-congestion-2.png -------------------------------------------------------------------------------- /assets/java-concurrency/thread-congestion/thread-congestion-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/thread-congestion/thread-congestion-3.png -------------------------------------------------------------------------------- /assets/java-concurrency/thread-congestion/thread-congestion-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/thread-congestion/thread-congestion-4.png -------------------------------------------------------------------------------- /assets/java-concurrency/tread-signaling/thread-signaling-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/tread-signaling/thread-signaling-1.png -------------------------------------------------------------------------------- /assets/java-concurrency/tread-signaling/thread-signaling-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/tread-signaling/thread-signaling-2.png -------------------------------------------------------------------------------- /assets/java-concurrency/virtual-threads/virtual-threads-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjenkov/java-examples/7ea0254a9315461c88fcada3f4c1644bb6d26130/assets/java-concurrency/virtual-threads/virtual-threads-1.png -------------------------------------------------------------------------------- /assets/toc.txt: -------------------------------------------------------------------------------- 1 | 2 | Deadlock Prevention & Detection 3 | 4 | - Lock Reordering 5 | 6 | - Timeout backoff 7 | 8 | - Deadlock detection 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/collections/SetOfExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.collections; 2 | 3 | import java.util.Set; 4 | 5 | public class SetOfExample { 6 | 7 | public static void main(String[] args) { 8 | Set strings = Set.of("123", "456", "789"); 9 | 10 | System.out.println(strings); 11 | 12 | strings.add("123"); 13 | 14 | System.out.println(strings); 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/AdditionalMethods.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | public class AdditionalMethods { 7 | 8 | public static void main(String[] args) { 9 | BlockingQueue blockingQueue = 10 | new ArrayBlockingQueue<>(3); 11 | 12 | int size = blockingQueue.size(); 13 | 14 | int capacity = blockingQueue.remainingCapacity(); 15 | 16 | boolean containsElement = 17 | blockingQueue.contains("1"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/BlockingQueueExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.LinkedBlockingQueue; 6 | 7 | public class BlockingQueueExample { 8 | 9 | public static void main(String[] args) { 10 | 11 | BlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3); 12 | BlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(); 13 | 14 | // java.util.concurrent.DelayQueue 15 | // java.util.concurrent.LinkedBlockingDeque 16 | // java.util.concurrent.LinkedTransferQueue 17 | // java.util.concurrent.PriorityBlockingQueue 18 | // java.util.concurrent.SynchronousQueue 19 | 20 | 21 | 22 | 23 | 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/BlockingQueueUsageExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.LinkedBlockingQueue; 6 | 7 | public class BlockingQueueUsageExample { 8 | 9 | public static void main(String[] args) throws InterruptedException { 10 | 11 | BlockingQueue blockingQueue = 12 | new ArrayBlockingQueue<>(3); 13 | 14 | blockingQueue.put("element 1"); 15 | blockingQueue.put("element 2"); 16 | 17 | String element1 = blockingQueue.take(); 18 | String element2 = blockingQueue.take(); 19 | 20 | System.out.println(element1); 21 | System.out.println(element2); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/Consumer.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | 5 | public class Consumer implements Runnable { 6 | BlockingQueue blockingQueue = null; 7 | 8 | public Consumer(BlockingQueue queue) { 9 | this.blockingQueue = queue; 10 | } 11 | 12 | @Override 13 | public void run() { 14 | while(true){ 15 | try { 16 | String element = 17 | this.blockingQueue.take(); 18 | String text = Thread.currentThread().getName() + 19 | " consumed " + element; 20 | System.out.println(text); 21 | } catch (InterruptedException e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/DequeueMethods.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class DequeueMethods { 8 | 9 | public static void main(String[] args) { 10 | BlockingQueue blockingQueue = 11 | new ArrayBlockingQueue<>(3); 12 | 13 | // take() blocks until an element becomes available 14 | try { 15 | String element = blockingQueue.take(); 16 | } catch (InterruptedException e) { 17 | e.printStackTrace(); 18 | } 19 | 20 | // poll() returns null if no element is available 21 | String element2 = blockingQueue.poll(); 22 | 23 | // poll(long timeout, TimeUnit timeUnit) blocks up until timeout 24 | // for an element to become available. If no element is available 25 | // before that time, null is returned. 26 | try { 27 | String element3 = blockingQueue.poll(1000, TimeUnit.MILLISECONDS); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | 32 | 33 | // removes the element if present in the BlockingQueue 34 | boolean wasRemoved = blockingQueue.remove("1"); 35 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/DrainMethods.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.concurrent.ArrayBlockingQueue; 6 | import java.util.concurrent.BlockingQueue; 7 | 8 | public class DrainMethods { 9 | 10 | public static void main(String[] args) { 11 | BlockingQueue blockingQueue = 12 | new ArrayBlockingQueue<>(10); 13 | 14 | Collection dest = new ArrayList(); 15 | 16 | blockingQueue.drainTo(dest); 17 | 18 | blockingQueue.drainTo(dest, 5); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/EnqueueMethods.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class EnqueueMethods { 8 | 9 | public static void main(String[] args) { 10 | BlockingQueue blockingQueue = 11 | new ArrayBlockingQueue<>(3); 12 | 13 | // put() will block until there is space 14 | // inside the BlockingQueue for the element 15 | try { 16 | blockingQueue.put("1"); 17 | } catch (InterruptedException e) { 18 | e.printStackTrace(); 19 | } 20 | 21 | // add will throw IllegalStateException if 22 | // no space available in BlockingQueue 23 | try{ 24 | blockingQueue.add("2"); 25 | } catch(IllegalStateException e) { 26 | // no space inside BlockingQueue 27 | } 28 | 29 | // offer() returns false if no space 30 | boolean wasEnqueued = blockingQueue.offer("3"); 31 | 32 | // offer(o, time, TimeUnit) blocks for time if no space, 33 | // then returns false if still no space. 34 | try { 35 | boolean wasEnqueued2 = 36 | blockingQueue.offer("3", 1000, TimeUnit.MILLISECONDS); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/PeekMethods.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.NoSuchElementException; 4 | import java.util.concurrent.ArrayBlockingQueue; 5 | import java.util.concurrent.BlockingQueue; 6 | 7 | public class PeekMethods { 8 | 9 | public static void main(String[] args) { 10 | BlockingQueue blockingQueue = 11 | new ArrayBlockingQueue<>(3); 12 | 13 | String element1 = blockingQueue.peek(); 14 | 15 | try { 16 | String element2 = blockingQueue.element(); 17 | } catch(NoSuchElementException e) { 18 | System.out.println("BlockingQueue is empty"); 19 | } 20 | 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/Producer.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | 5 | public class Producer implements Runnable { 6 | BlockingQueue blockingQueue = null; 7 | 8 | public Producer(BlockingQueue queue) { 9 | this.blockingQueue = queue; 10 | } 11 | 12 | @Override 13 | public void run() { 14 | while(true) { 15 | long timeMillis = System.currentTimeMillis(); 16 | try { 17 | this.blockingQueue.put("" + timeMillis); 18 | } catch (InterruptedException e) { 19 | System.out.println("Producer was interrupted"); 20 | } 21 | sleep(1000); 22 | } 23 | } 24 | 25 | private void sleep(long timeMillis) { 26 | try { 27 | Thread.sleep(timeMillis); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/blockingqueue/ProducerConsumerExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.blockingqueue; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | public class ProducerConsumerExample { 7 | 8 | public static void main(String[] args) { 9 | BlockingQueue blockingQueue = 10 | new ArrayBlockingQueue<>(3); 11 | 12 | Producer producer = new Producer(blockingQueue); 13 | Consumer consumer1 = new Consumer(blockingQueue); 14 | Consumer consumer2 = new Consumer(blockingQueue); 15 | 16 | Thread producerThread = new Thread(producer); 17 | Thread consumerThread1 = new Thread(consumer1); 18 | Thread consumerThread2 = new Thread(consumer2); 19 | producerThread.start(); 20 | consumerThread1.start(); 21 | consumerThread2.start(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/compareandswap/CompareAndSwapExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.compareandswap; 2 | 3 | public class CompareAndSwapExample { 4 | 5 | public static void main(String[] args) { 6 | 7 | Counter counter = new SimpleCounter(new ProblematicLock()); 8 | //Counter counter = new SimpleCounter(new CompareAndSwapLock()); 9 | //Counter counter = new OptimisticLockCounter(); 10 | 11 | CountRunnable runnable1 = new CountRunnable(counter, 1_000_000); 12 | CountRunnable runnable2 = new CountRunnable(counter, 1_000_000); 13 | 14 | Thread thread1 = new Thread(runnable1); 15 | Thread thread2 = new Thread(runnable2); 16 | 17 | thread1.start(); 18 | thread2.start(); 19 | 20 | 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/compareandswap/CompareAndSwapLock.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.compareandswap; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | public class CompareAndSwapLock implements MyLock{ 6 | 7 | private AtomicBoolean atomicLocked = new AtomicBoolean(false); 8 | 9 | 10 | public void unlock() { 11 | this.atomicLocked.set(false); 12 | } 13 | 14 | public void lock() { 15 | while(!this.atomicLocked.compareAndSet(false, true)) { 16 | // busy wait - until compareAndSet() succeeds 17 | } 18 | 19 | 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/compareandswap/CountRunnable.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.compareandswap; 2 | 3 | public class CountRunnable implements Runnable { 4 | 5 | private Counter counter = null; 6 | private long iterations = 0; 7 | 8 | public CountRunnable(Counter counter, long iterations) { 9 | this.counter = counter; 10 | this.iterations = iterations; 11 | } 12 | 13 | @Override 14 | public void run() { 15 | for(int i=0; i involvedThreadNodes = new HashSet<>(); 10 | 11 | while(true) { 12 | ThreadNode lockedByThread = lockNode.lockedBy; 13 | 14 | if(involvedThreadNodes.contains(lockedByThread)) { 15 | return true; 16 | } 17 | involvedThreadNodes.add(lockedByThread); 18 | 19 | if(lockedByThread.waitingFor == null) { 20 | return false; 21 | } 22 | lockNode = lockedByThread.waitingFor; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/DeadlockDetectionExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | public class DeadlockDetectionExample { 4 | 5 | public static void main(String[] args) { 6 | 7 | LockNode lockNode1 = new LockNode(); 8 | ThreadNode threadNode1 = new ThreadNode(); 9 | lockNode1.lockedBy = threadNode1; 10 | 11 | LockNode lockNode2 = new LockNode(); 12 | ThreadNode threadNode2 = new ThreadNode(); 13 | lockNode2.lockedBy = threadNode2; 14 | 15 | LockNode lockNode3 = new LockNode(); 16 | ThreadNode threadNode3 = new ThreadNode(); 17 | lockNode3.lockedBy = threadNode3; 18 | 19 | threadNode1.waitingFor = lockNode2; 20 | //threadNode2.waitingFor = lockNode3; 21 | threadNode3.waitingFor = lockNode1; 22 | 23 | 24 | DeadlockDetection deadlockDetection = new DeadlockDetection(); 25 | 26 | boolean involvedInDeadlock1 = deadlockDetection.isInvolvedInDeadlock(lockNode1); 27 | boolean involvedInDeadlock2 = deadlockDetection.isInvolvedInDeadlock(lockNode2); 28 | boolean involvedInDeadlock3 = deadlockDetection.isInvolvedInDeadlock(lockNode3); 29 | 30 | System.out.println(involvedInDeadlock1); 31 | System.out.println(involvedInDeadlock2); 32 | System.out.println(involvedInDeadlock3); 33 | 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/LockGraphFacade.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.locks.Lock; 6 | 7 | public class LockGraphFacade { 8 | 9 | private DeadlockDetection deadlockDetection = new DeadlockDetection(); 10 | 11 | private Map threadNodes = new HashMap<>(); 12 | private Map lockNodes = new HashMap<>(); 13 | 14 | public synchronized boolean tryLock(Lock lock) { 15 | Thread currentThread = Thread.currentThread(); 16 | return tryLock(lock, currentThread); 17 | } 18 | 19 | public synchronized boolean tryLock(Lock lock, Thread currentThread) { 20 | System.out.println(currentThread.getName() + " tryLock()..."); 21 | LockNode lockNode = getOrCreateLockNode(lock); 22 | 23 | ThreadNode threadNode = getOrCreateThreadNode(currentThread); 24 | 25 | if(lockNode.lockedBy == threadNode) { 26 | return true; 27 | } 28 | if(lockNode.lockedBy == null) { 29 | lockNode.lockedBy = threadNode; 30 | lock.lock(); 31 | return true; 32 | } 33 | 34 | threadNode.waitingFor = lockNode; 35 | 36 | if(this.deadlockDetection.isInvolvedInDeadlock(lockNode)){ 37 | threadNode.waitingFor = null; 38 | return false; 39 | } 40 | 41 | // Lock is locked by another thread, but is not part of a deadlock, 42 | // so it is safe to wait to lock the Lock at a later time. 43 | lock.lock(); 44 | threadNode.waitingFor = null; 45 | lockNode.lockedBy = threadNode; 46 | return true; 47 | } 48 | 49 | public synchronized void unlock(Lock lock) { 50 | lock.unlock(); 51 | LockNode lockNode = getOrCreateLockNode(lock); 52 | lockNode.lockedBy = null; 53 | } 54 | 55 | private LockNode getOrCreateLockNode(Lock lock) { 56 | LockNode lockNode = this.lockNodes.get(lock); 57 | if(lockNode == null) { 58 | lockNode = new LockNode(); 59 | this.lockNodes.put(lock, lockNode); 60 | } 61 | return lockNode; 62 | } 63 | 64 | private ThreadNode getOrCreateThreadNode(Thread thread) { 65 | ThreadNode threadNode = this.threadNodes.get(thread); 66 | if(threadNode == null) { 67 | threadNode = new ThreadNode(); 68 | threadNode.thread = thread; 69 | this.threadNodes.put(thread, threadNode); 70 | } 71 | return threadNode; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/LockGraphFacadeExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | 6 | public class LockGraphFacadeExample { 7 | 8 | public static void main(String[] args) { 9 | withThreads(); 10 | } 11 | 12 | private static void withThreads() { 13 | LockGraphFacade lockGraphFacade = new LockGraphFacade(); 14 | 15 | Lock lock1 = new ReentrantLock(); 16 | Lock lock2 = new ReentrantLock(); 17 | 18 | Runnable1DeadlockDetection runnable1 = 19 | new Runnable1DeadlockDetection(lockGraphFacade, lock1, lock2); 20 | Runnable2DeadlockDetection runnable2 = 21 | new Runnable2DeadlockDetection(lockGraphFacade, lock1, lock2); 22 | 23 | Thread thread1 = new Thread(runnable1); 24 | Thread thread2 = new Thread(runnable2); 25 | thread1.start(); 26 | thread2.start(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/LockNode.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | public class LockNode { 4 | 5 | public ThreadNode lockedBy = null; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/Runnable1DeadlockDetection.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.locks.Lock; 5 | 6 | public class Runnable1DeadlockDetection implements Runnable{ 7 | 8 | private LockGraphFacade lockGraphFacade = null; 9 | private Lock lock1 = null; 10 | private Lock lock2 = null; 11 | 12 | public Runnable1DeadlockDetection(LockGraphFacade lockGraphFacade, Lock lock1, Lock lock2) { 13 | this.lockGraphFacade = lockGraphFacade; 14 | this.lock1 = lock1; 15 | this.lock2 = lock2; 16 | } 17 | 18 | @Override 19 | public void run() { 20 | String threadName = Thread.currentThread().getName(); 21 | 22 | while(true) { 23 | int failureCount = 0; 24 | while(! tryLockBothLocks()) { 25 | failureCount++; 26 | System.err.println(threadName + " failed to lock both Locks. " + 27 | "Waiting a bit before retrying [" + failureCount + " tries]"); 28 | sleep(100L * ((long) Math.random())); 29 | } 30 | if(failureCount > 0) { 31 | System.out.println(threadName + 32 | " succeeded in locking both locks - after " + failureCount + " failures."); 33 | } 34 | 35 | //do the work - now that both locks have been acquired (locked by this thread) 36 | 37 | //unlock 38 | this.lockGraphFacade.unlock(this.lock2); 39 | this.lockGraphFacade.unlock(this.lock1); 40 | } 41 | } 42 | 43 | 44 | 45 | private boolean tryLockBothLocks() { 46 | String threadName = Thread.currentThread().getName(); 47 | 48 | System.out.println(threadName + " lock1: attempt lock"); 49 | boolean lock1Succeeded = this.lockGraphFacade.tryLock(this.lock1); 50 | if(!lock1Succeeded) { 51 | return false; 52 | } 53 | System.out.println(threadName + " lock1: locked"); 54 | 55 | sleep(1000); 56 | 57 | System.out.println(threadName + " lock2: attempt lock"); 58 | boolean lock2Succeeded = this.lockGraphFacade.tryLock(this.lock2); 59 | if(!lock2Succeeded) { 60 | this.lockGraphFacade.unlock(lock1); 61 | return false; 62 | } 63 | System.out.println(threadName + " lock2: locked"); 64 | 65 | return true; 66 | } 67 | 68 | private void sleep(long timeMillis) { 69 | try { 70 | Thread.sleep(timeMillis); 71 | } catch (InterruptedException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/Runnable2DeadlockDetection.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | 5 | public class Runnable2DeadlockDetection implements Runnable{ 6 | 7 | private LockGraphFacade lockGraphFacade = null; 8 | private Lock lock1 = null; 9 | private Lock lock2 = null; 10 | 11 | public Runnable2DeadlockDetection(LockGraphFacade lockGraphFacade, Lock lock1, Lock lock2) { 12 | this.lockGraphFacade = lockGraphFacade; 13 | this.lock1 = lock1; 14 | this.lock2 = lock2; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | String threadName = Thread.currentThread().getName(); 20 | 21 | while(true) { 22 | int failureCount = 0; 23 | while(! tryLockBothLocks()) { 24 | failureCount++; 25 | System.err.println(threadName + " failed to lock both Locks. " + 26 | "Waiting a bit before retrying [" + failureCount + " tries]"); 27 | sleep(100L * ((long) Math.random())); 28 | } 29 | if(failureCount > 0) { 30 | System.out.println(threadName + 31 | " succeeded in locking both locks - after " + failureCount + " failures."); 32 | } 33 | 34 | //do the work - now that both locks have been acquired (locked by this thread) 35 | 36 | //unlock 37 | this.lockGraphFacade.unlock(this.lock2); 38 | this.lockGraphFacade.unlock(this.lock1); 39 | } 40 | } 41 | 42 | private void sleep(long timeMillis) { 43 | try { 44 | Thread.sleep(timeMillis); 45 | } catch (InterruptedException e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | 50 | private boolean tryLockBothLocks() { 51 | String threadName = Thread.currentThread().getName(); 52 | 53 | System.out.println(threadName + " lock2: attempt lock"); 54 | boolean lock2Succeeded = this.lockGraphFacade.tryLock(this.lock2); 55 | if(!lock2Succeeded) { 56 | return false; 57 | } 58 | System.out.println(threadName + " lock2: locked"); 59 | 60 | sleep(1000); 61 | 62 | System.out.println(threadName + " lock1: attempt lock"); 63 | boolean lock1Succeeded = this.lockGraphFacade.tryLock(this.lock1); 64 | if(!lock1Succeeded) { 65 | this.lockGraphFacade.unlock(lock2); 66 | return false; 67 | } 68 | System.out.println(threadName + " lock1: locked"); 69 | 70 | return true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/detection/ThreadNode.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.detection; 2 | 3 | public class ThreadNode { 4 | 5 | public LockNode waitingFor = null; 6 | public Thread thread = null; 7 | 8 | 9 | 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/prevention/DeadlockTimeoutExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.prevention; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | 6 | public class DeadlockTimeoutExample { 7 | 8 | public static void main(String[] args) { 9 | 10 | Lock lock1 = new ReentrantLock(); 11 | Lock lock2 = new ReentrantLock(); 12 | 13 | Runnable runnable1 = new Runnable1TimeOut(lock1, lock2); 14 | Runnable runnable2 = new Runnable2TimeOut(lock1, lock2); 15 | 16 | Thread thread1 = new Thread(runnable1); 17 | Thread thread2 = new Thread(runnable2); 18 | 19 | thread1.start(); 20 | thread2.start(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/prevention/Runnable1TimeOut.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.prevention; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.locks.Lock; 5 | 6 | public class Runnable1TimeOut implements Runnable{ 7 | 8 | private Lock lock1 = null; 9 | private Lock lock2 = null; 10 | 11 | public Runnable1TimeOut(Lock lock1, Lock lock2) { 12 | this.lock1 = lock1; 13 | this.lock2 = lock2; 14 | } 15 | 16 | @Override 17 | public void run() { 18 | String threadName = Thread.currentThread().getName(); 19 | 20 | while(true) { 21 | int failureCount = 0; 22 | while(! tryLockBothLocks()) { 23 | failureCount++; 24 | System.err.println(threadName + " failed to lock both Locks. " + 25 | "Waiting a bit before retrying [" + failureCount + " tries]"); 26 | sleep(100L * ((long) Math.random())); 27 | } 28 | if(failureCount > 0) { 29 | System.out.println(threadName + 30 | " succeeded in locking both locks - after " + failureCount + " failures."); 31 | } 32 | 33 | //do the work - now that both locks have been acquired (locked by this thread) 34 | 35 | //unlock 36 | lock2.unlock(); 37 | lock1.unlock(); 38 | } 39 | } 40 | 41 | private void sleep(long timeMillis) { 42 | try { 43 | Thread.sleep(timeMillis); 44 | } catch (InterruptedException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | 49 | private boolean tryLockBothLocks() { 50 | String threadName = Thread.currentThread().getName(); 51 | 52 | try { 53 | boolean lock1Succeeded = lock1.tryLock(1000, TimeUnit.MILLISECONDS); 54 | if(!lock1Succeeded) { 55 | return false; 56 | } 57 | } catch (InterruptedException e) { 58 | System.out.println(threadName + " interrupted trying to lock Lock 1"); 59 | return false; 60 | } 61 | try { 62 | boolean lock2Succeeded = lock2.tryLock(1000, TimeUnit.MILLISECONDS); 63 | if(!lock2Succeeded) { 64 | lock1.unlock(); 65 | return false; 66 | } 67 | } catch (InterruptedException e) { 68 | System.out.println(threadName + " interrupted trying to lock Lock 2"); 69 | lock1.unlock(); 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/deadlock/prevention/Runnable2TimeOut.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.deadlock.prevention; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.locks.Lock; 5 | 6 | public class Runnable2TimeOut implements Runnable{ 7 | 8 | private Lock lock1 = null; 9 | private Lock lock2 = null; 10 | 11 | public Runnable2TimeOut(Lock lock1, Lock lock2) { 12 | this.lock1 = lock1; 13 | this.lock2 = lock2; 14 | } 15 | 16 | @Override 17 | public void run() { 18 | String threadName = Thread.currentThread().getName(); 19 | 20 | while(true) { 21 | int failureCount = 0; 22 | while(! tryLockBothLocks()) { 23 | failureCount++; 24 | System.err.println(threadName + " failed to lock both Locks. Waiting a bit before retrying [" + failureCount + " tries]"); 25 | sleep(100L * ((long) Math.random())); 26 | } 27 | if(failureCount > 0) { 28 | System.out.println(threadName + " succeeded in locking both locks - after " + failureCount + " failures."); 29 | } 30 | 31 | //do the work - now that both locks have been acquired (locked by this thread) 32 | 33 | //unlock 34 | lock2.unlock(); 35 | lock1.unlock(); 36 | } 37 | } 38 | 39 | private void sleep(long timeMillis) { 40 | try { 41 | Thread.sleep(timeMillis); 42 | } catch (InterruptedException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | private boolean tryLockBothLocks() { 48 | String threadName = Thread.currentThread().getName(); 49 | 50 | try { 51 | boolean lock2Succeeded = lock2.tryLock(1000, TimeUnit.MILLISECONDS); 52 | if(!lock2Succeeded) { 53 | return false; 54 | } 55 | } catch (InterruptedException e) { 56 | System.out.println(threadName + " interrupted trying to lock Lock 2"); 57 | return false; 58 | } 59 | try { 60 | boolean lock1Succeeded = lock1.tryLock(1000, TimeUnit.MILLISECONDS); 61 | if(!lock1Succeeded) { 62 | lock2.unlock(); 63 | return false; 64 | } 65 | } catch (InterruptedException e) { 66 | System.out.println(threadName + " interrupted trying to lock Lock 1"); 67 | lock1.unlock(); 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/falsesharing/Counter1.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.falsesharing; 2 | 3 | 4 | public class Counter1 { 5 | 6 | public volatile long count1 = 0; 7 | 8 | public volatile long count2 = 0; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/falsesharing/Counter2.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.falsesharing; 2 | 3 | 4 | public class Counter2 { 5 | 6 | @jdk.internal.vm.annotation.Contended 7 | public volatile long count1 = 0; 8 | //padding bytes 9 | public volatile long count2 = 0; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/falsesharing/Counter3.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.falsesharing; 2 | 3 | 4 | @jdk.internal.vm.annotation.Contended 5 | public class Counter3 { 6 | 7 | public volatile long count1 = 0; 8 | //padding bytes 9 | 10 | public volatile long count2 = 0; 11 | //padding bytes 12 | 13 | public volatile long count3 = 0; 14 | //padding bytes 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/falsesharing/Counter4.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.falsesharing; 2 | 3 | 4 | public class Counter4 { 5 | 6 | @jdk.internal.vm.annotation.Contended("group1") 7 | public volatile long count1 = 0; 8 | 9 | @jdk.internal.vm.annotation.Contended("group1") 10 | public volatile long count2 = 0; 11 | 12 | @jdk.internal.vm.annotation.Contended("group2") 13 | public volatile long count3 = 0; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/falsesharing/FalseSharingExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.falsesharing; 2 | 3 | public class FalseSharingExample { 4 | 5 | public static void main(String[] args) { 6 | 7 | Counter3 counter1 = new Counter3(); 8 | Counter3 counter2 = counter1; 9 | //Counter2 counter2 = new Counter1(); 10 | 11 | 12 | long iterations = 1_000_000_000; 13 | 14 | Thread thread1 = new Thread(() -> { 15 | long startTime = System.currentTimeMillis(); 16 | for(long i=0; i { 23 | long startTime = System.currentTimeMillis(); 24 | for(long i=0; i forkJoinTask = forkJoinPool1.submit(myRecursiveTask); 20 | try { 21 | Long result = forkJoinTask.get(); 22 | } catch (InterruptedException e) { 23 | throw new RuntimeException(e); 24 | } catch (ExecutionException e) { 25 | throw new RuntimeException(e); 26 | } 27 | 28 | forkJoinPool1.getParallelism(); 29 | forkJoinPool1.setParallelism(10); 30 | forkJoinPool1.getPoolSize(); 31 | forkJoinPool1.getQueuedSubmissionCount(); 32 | forkJoinPool1.getRunningThreadCount(); 33 | forkJoinPool1.isShutdown(); 34 | forkJoinPool1.isTerminated(); 35 | forkJoinPool1.isTerminating(); 36 | 37 | forkJoinPool1.shutdown(); 38 | forkJoinPool1.shutdownNow(); 39 | 40 | 41 | //System.out.println("Result: "+ result); 42 | sleep(1000); // Make sure all System.out.print() reaches console before app stops. 43 | } 44 | 45 | private static void sleep(long millis) { 46 | try { 47 | Thread.sleep(millis); 48 | } catch (InterruptedException e) { 49 | throw new RuntimeException(e); 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/forkjoinpool/JavaForkJoinPoolExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.forkjoinpool; 2 | 3 | import java.util.concurrent.ForkJoinPool; 4 | 5 | public class JavaForkJoinPoolExample { 6 | 7 | public static void main(String[] args) { 8 | ForkJoinPool forkJoinPool1 = ForkJoinPool.commonPool(); 9 | ForkJoinPool forkJoinPool2 = new ForkJoinPool(4); 10 | 11 | MyRecursiveAction myRecursiveAction = new MyRecursiveAction(123); 12 | forkJoinPool1.invoke(myRecursiveAction); 13 | 14 | MyRecursiveTask myRecursiveTask = new MyRecursiveTask(123); 15 | //long result = forkJoinPool1.invoke(myRecursiveTask); 16 | 17 | //System.out.println("Result: "+ result); 18 | sleep(1000); // Make sure all System.out.print() reaches console before app stops. 19 | } 20 | 21 | private static void sleep(long millis) { 22 | try { 23 | Thread.sleep(millis); 24 | } catch (InterruptedException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/forkjoinpool/MyRecursiveAction.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.forkjoinpool; 2 | 3 | import java.util.concurrent.RecursiveAction; 4 | 5 | public class MyRecursiveAction extends RecursiveAction { 6 | 7 | private long workLoad = 0; 8 | 9 | public MyRecursiveAction(long workLoad) { 10 | this.workLoad = workLoad; 11 | } 12 | 13 | @Override 14 | protected void compute() { 15 | //if work is above threshold, break tasks up into smaller tasks 16 | if(this.workLoad > 16) { 17 | System.out.println("Splitting workLoad : " + this.workLoad); 18 | 19 | long workload1 = this.workLoad / 2; 20 | long workload2 = this.workLoad - workload1; 21 | 22 | MyRecursiveAction subtask1 = new MyRecursiveAction(workload1); 23 | MyRecursiveAction subtask2 = new MyRecursiveAction(workload2); 24 | 25 | subtask1.fork(); 26 | subtask2.fork(); 27 | 28 | } else { 29 | System.out.println("Doing workLoad myself: " + this.workLoad); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/forkjoinpool/MyRecursiveTask.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.forkjoinpool; 2 | 3 | import java.util.concurrent.RecursiveTask; 4 | 5 | public class MyRecursiveTask extends RecursiveTask { 6 | 7 | private long workLoad = 0; 8 | 9 | public MyRecursiveTask(long workLoad) { 10 | this.workLoad = workLoad; 11 | } 12 | 13 | protected Long compute() { 14 | 15 | //if work is above threshold, break tasks up into smaller tasks 16 | if(this.workLoad > 16) { 17 | System.out.println("Splitting workLoad : " + this.workLoad); 18 | 19 | long workload1 = this.workLoad / 2; 20 | long workload2 = this.workLoad - workload1; 21 | 22 | MyRecursiveTask subtask1 = new MyRecursiveTask(workload1); 23 | MyRecursiveTask subtask2 = new MyRecursiveTask(workload2); 24 | 25 | subtask1.fork(); 26 | subtask2.fork(); 27 | 28 | long result = 0; 29 | result += subtask1.join(); 30 | result += subtask2.join(); 31 | return result; 32 | 33 | } else { 34 | System.out.println("Doing workLoad myself: " + this.workLoad); 35 | return workLoad * 3; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/BackgroundRunnable.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | public class BackgroundRunnable implements Runnable { 4 | 5 | @Override 6 | public void run() { 7 | 8 | } 9 | 10 | public void stop() { 11 | 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/ForegroundRunnable.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | public class ForegroundRunnable implements Runnable { 4 | 5 | @Override 6 | public void run() { 7 | 8 | } 9 | 10 | public void stop() { 11 | 12 | } 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/IAgent.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | public interface IAgent { 4 | 5 | 6 | //todo requestTermination() ?? 7 | 8 | 9 | public boolean isTerminated(); 10 | 11 | public void continueExec(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/Main.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | 7 | // *) Agents transferring themselves or other agents to a different thread 8 | // - ThreadSystem.transferToForegroundThread(IAgent) 9 | // - ThreadSystem.transferToBackgroundThread(IAgent) 10 | 11 | // *) Sub agents releasing messages / results to a parent agent 12 | // - IAgent.getResultQueue() 13 | 14 | // -------------------------------------------------------------------------- 15 | 16 | // *) Agent switcher ... like a one-off task executor switching between tasks 17 | // - switcher.addAgent(IAgent agent) 18 | // - switcher.continue(increments) 19 | 20 | // *) Agent switcher - prioritized - so some agents are allowed to execute a higher number of increments than others. 21 | 22 | 23 | 24 | ThreadSystem threadSystem = new ThreadSystem(); 25 | 26 | threadSystem.addForegroundThread().submitAgent(new PrintAgent("PA 0", threadSystem)); 27 | threadSystem.addForegroundThread().submitAgent(new PrintAgent("PA 1", threadSystem)); 28 | 29 | threadSystem.addBackgroundThread(); 30 | 31 | threadSystem.start(); 32 | 33 | Thread.sleep(1000); 34 | 35 | threadSystem.requestTermination(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/PrintAgent.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class PrintAgent implements IAgent { 7 | 8 | private String agentName = null; 9 | private AtomicBoolean isTerminated = new AtomicBoolean(false); 10 | 11 | private ThreadSystem threadSystem = null; 12 | 13 | private AtomicInteger agentState = new AtomicInteger(0); 14 | 15 | public PrintAgent() {} 16 | public PrintAgent(String agentName, ThreadSystem threadSystem) { 17 | this.agentName = agentName; 18 | this.threadSystem = threadSystem; 19 | } 20 | 21 | @Override 22 | public boolean isTerminated() { 23 | return this.isTerminated.get(); 24 | } 25 | 26 | @Override 27 | public void continueExec() { 28 | String threadPlusAgentName = Thread.currentThread().getName() + " " + this.agentName; 29 | 30 | System.out.println(threadPlusAgentName + " running in foreground thread." ); 31 | 32 | SubAgent subAgent = new SubAgent(); 33 | try { 34 | this.threadSystem.submitToBackgroundThread(subAgent); 35 | } catch (InterruptedException e) { 36 | e.printStackTrace(); 37 | } 38 | 39 | for(int i=0; i<10; i++) { 40 | System.out.println(threadPlusAgentName + " Print " + i); 41 | 42 | sleep(500); 43 | } 44 | 45 | while(subAgent.getMessageQueue().size() > 0) { 46 | try { 47 | String message = (String) subAgent.getMessageQueue().take(); 48 | System.out.println(threadPlusAgentName + " from SubAgent: " + message); 49 | } catch (InterruptedException e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | 54 | this.isTerminated.set(true); 55 | } 56 | 57 | private void sleep(int sleepIntervalMillis) { 58 | try { 59 | Thread.sleep(sleepIntervalMillis); 60 | } catch (InterruptedException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/SubAgent.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | public class SubAgent implements IAgent { 8 | 9 | 10 | private BlockingQueue messageQueue = new ArrayBlockingQueue(16); 11 | 12 | 13 | private int increments = 5; 14 | 15 | private AtomicBoolean isTerminated = new AtomicBoolean(false); 16 | 17 | 18 | 19 | @Override 20 | public boolean isTerminated() { 21 | return this.isTerminated.get(); 22 | } 23 | 24 | @Override 25 | public void continueExec() { 26 | System.out.println("SubAgent running: " + this.increments); 27 | 28 | try { 29 | getMessageQueue().put("" + this.increments); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | 34 | this.increments--; 35 | this.isTerminated.set(this.increments <= 0); 36 | } 37 | 38 | public BlockingQueue getMessageQueue() { 39 | return this.messageQueue; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/SystemThreadRunnable.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | 4 | import java.util.concurrent.ArrayBlockingQueue; 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | 9 | /** 10 | * A SystemThreadRunnable should be executing an IAgent instance repeatedly until one of the following conditions are true: 11 | * 12 | * 13 | * 1) The agent has terminated. 14 | * 15 | * 2) The SystemThreadRunnable has been signaled to terminate 16 | * 17 | * 18 | * If the agent being executed has terminated, the SystemThreadRunnable can pull a new IAgent instance from its 19 | * inbound queue of IAgents, and continue executing that IAgent instance. 20 | * 21 | */ 22 | public class SystemThreadRunnable implements Runnable { 23 | 24 | 25 | private BlockingQueue agentQueue = new ArrayBlockingQueue(1024); 26 | private AtomicBoolean shouldTerminate = new AtomicBoolean(false); 27 | 28 | public SystemThreadRunnable submitAgent(IAgent agent) throws InterruptedException { 29 | this.agentQueue.put(agent); 30 | return this; 31 | } 32 | 33 | public void requestTermination() { 34 | this.shouldTerminate.set(true); 35 | } 36 | 37 | @Override 38 | public void run() { 39 | while(!shouldTerminate.get()){ 40 | 41 | try { 42 | IAgent nextAgent = this.agentQueue.poll(1000, TimeUnit.MILLISECONDS); 43 | 44 | if(nextAgent != null) { 45 | while(!nextAgent.isTerminated()) { 46 | nextAgent.continueExec(); 47 | } 48 | } 49 | 50 | } catch (InterruptedException e) { 51 | // ignore 52 | //e.printStackTrace(); 53 | } 54 | } 55 | System.out.println(Thread.currentThread().getName() + " Terminated"); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/samethreading/ThreadSystem.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.samethreading; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class ThreadSystem { 7 | 8 | protected List foregroundThreads = new ArrayList<>(); 9 | protected List backgroundThreads = new ArrayList<>(); 10 | 11 | protected int nextForegroundThreadToMoveTo = 0; 12 | protected int nextBackgroundThreadToMoveTo = 0; 13 | 14 | 15 | public SystemThreadRunnable addForegroundThread() { 16 | SystemThreadRunnable foregroundThread = new SystemThreadRunnable(); 17 | this.foregroundThreads.add(foregroundThread); 18 | return foregroundThread; 19 | } 20 | 21 | public SystemThreadRunnable addBackgroundThread() { 22 | SystemThreadRunnable backgroundThread = new SystemThreadRunnable(); 23 | this.backgroundThreads.add(backgroundThread); 24 | return backgroundThread; 25 | } 26 | 27 | public ThreadSystem start() { 28 | 29 | for(SystemThreadRunnable foregroundThread : this.foregroundThreads) { 30 | Thread thread = new Thread(foregroundThread); 31 | thread.start(); 32 | } 33 | 34 | for(SystemThreadRunnable backgroundThread : this.backgroundThreads) { 35 | Thread thread = new Thread(backgroundThread); 36 | thread.start(); 37 | } 38 | 39 | return this; 40 | } 41 | 42 | public ThreadSystem requestTermination() { 43 | 44 | for(SystemThreadRunnable foregroundThread : this.foregroundThreads) { 45 | foregroundThread.requestTermination(); 46 | } 47 | 48 | for(SystemThreadRunnable backgroundThread : this.backgroundThreads) { 49 | backgroundThread.requestTermination(); 50 | } 51 | 52 | return this; 53 | } 54 | 55 | public void submitToBackgroundThread(IAgent agent) throws InterruptedException { 56 | SystemThreadRunnable backgroundThread = this.backgroundThreads.get(this.nextBackgroundThreadToMoveTo); 57 | backgroundThread.submitAgent(agent); 58 | 59 | this.nextBackgroundThreadToMoveTo++; 60 | this.nextBackgroundThreadToMoveTo = this.nextBackgroundThreadToMoveTo % this.backgroundThreads.size(); 61 | } 62 | 63 | public void submitToForegroundThread(IAgent agent) throws InterruptedException { 64 | SystemThreadRunnable foregroundThread = this.foregroundThreads.get(this.nextForegroundThreadToMoveTo); 65 | foregroundThread.submitAgent(agent); 66 | 67 | this.nextForegroundThreadToMoveTo++; 68 | this.nextForegroundThreadToMoveTo = this.nextForegroundThreadToMoveTo % this.foregroundThreads.size(); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadcongestion/ConsumerRunnable.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadcongestion; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | public class ConsumerRunnable implements Runnable { 8 | 9 | private BlockingQueue blockingQueue = null; 10 | 11 | private AtomicBoolean keepRunning = new AtomicBoolean(true); 12 | 13 | public ConsumerRunnable(BlockingQueue blockingQueue) { 14 | this.blockingQueue = blockingQueue; 15 | } 16 | 17 | public void stop() { 18 | System.out.println("Stopped thread"); 19 | this.keepRunning.set(false); 20 | } 21 | 22 | @Override 23 | public void run() { 24 | 25 | System.out.println(Thread.currentThread().getName() + " consumer started."); 26 | 27 | long objectsConsumed = 0; 28 | while (this.keepRunning.get()) { 29 | String obj = takeObjectFromQueue(); 30 | if(obj != null) { objectsConsumed++; } 31 | } 32 | System.out.println(Thread.currentThread().getName() + " finishing up"); 33 | while (this.blockingQueue.size() > 0) { 34 | String obj = takeObjectFromQueue(); 35 | if(obj != null) { objectsConsumed++; } 36 | } 37 | System.out.println(Thread.currentThread().getName() + 38 | " consumer finished: " + objectsConsumed); 39 | } 40 | 41 | private String takeObjectFromQueue() { 42 | try { 43 | return blockingQueue.poll(1000, TimeUnit.MILLISECONDS); 44 | } catch (InterruptedException e) { 45 | e.printStackTrace(); 46 | return null; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadcongestion/ThreadCongestionExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadcongestion; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | public class ThreadCongestionExample { 7 | 8 | public static void main(String[] args) { 9 | int objectsToProduce = 1_000_000; 10 | 11 | BlockingQueue blockingQueue = 12 | new ArrayBlockingQueue(objectsToProduce); 13 | 14 | ConsumerRunnable[] consumerRunnables = 15 | new ConsumerRunnable[3]; 16 | 17 | synchronized (ThreadCongestionExample.class){ 18 | for(int i=0; i { 26 | for(int i=0; i produced " + objectsToProduce); 35 | synchronized (ThreadCongestionExample.class) { 36 | for (int i = 0; i < consumerRunnables.length; i++) { 37 | consumerRunnables[i].stop(); 38 | } 39 | } 40 | }); 41 | producingThread.start(); 42 | 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadcongestion/ThreadCongestionExample2.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadcongestion; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | public class ThreadCongestionExample2 { 7 | 8 | public static void main(String[] args) { 9 | int objectsToProduce = 1_000_000; 10 | 11 | BlockingQueue[] blockingQueues = new BlockingQueue[3]; 12 | for(int i=0; i(objectsToProduce); 14 | } 15 | 16 | ConsumerRunnable[] consumerRunnables = new ConsumerRunnable[3]; 17 | synchronized (ThreadCongestionExample2.class){ 18 | for(int i=0; i { 26 | for(int i=0; i produced " + objectsToProduce); 35 | 36 | synchronized (ThreadCongestionExample2.class) { 37 | for(int i=0; i " + output); 47 | } catch (InterruptedException e) { 48 | if(!this.shutdownRequested) { 49 | System.out.println("Step 1: Failed for input " + input); 50 | e.printStackTrace(); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadpipeline/Step2Processor.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadpipeline; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | public class Step2Processor implements Processor { 7 | 8 | private BlockingQueue inputQueue = new ArrayBlockingQueue(10); 9 | 10 | private BlockingQueue outputQueue = null; 11 | 12 | private volatile boolean shutdownRequested = false; 13 | 14 | private volatile Thread executingThread = null; 15 | 16 | 17 | @Override 18 | public BlockingQueue setInputQueue(BlockingQueue inputQueue) { 19 | this.inputQueue = inputQueue; 20 | return this.inputQueue; 21 | } 22 | 23 | @Override 24 | public BlockingQueue setOutputQueue(BlockingQueue outputQueue) { 25 | this.outputQueue = outputQueue; 26 | return this.outputQueue; 27 | } 28 | 29 | @Override 30 | public void requestShutdown() { 31 | this.shutdownRequested = true; 32 | this.executingThread.interrupt(); 33 | } 34 | 35 | @Override 36 | public void run() { 37 | 38 | this.executingThread = Thread.currentThread(); 39 | while(!this.shutdownRequested){ 40 | Object input = null; 41 | try { 42 | input = this.inputQueue.take(); 43 | 44 | Object output = input.toString().substring(0, 7); 45 | 46 | this.outputQueue.put(output); 47 | 48 | //System.out.println("Step 2: " + input + " => " + output); 49 | } catch (InterruptedException e) { 50 | if(!this.shutdownRequested){ 51 | System.out.println("Step 2: Failed for input " + input); 52 | e.printStackTrace(); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadpipeline/ThreadPipeline.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadpipeline; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ArrayBlockingQueue; 6 | import java.util.concurrent.BlockingQueue; 7 | 8 | public class ThreadPipeline { 9 | 10 | private List processors = new ArrayList<>(); 11 | 12 | private BlockingQueue inputQueue = null; 13 | private BlockingQueue outputQueue = null; 14 | 15 | public ThreadPipeline addProcessor(Processor processor){ 16 | return this; 17 | } 18 | 19 | public BlockingQueue getInputQueue() { 20 | return this.inputQueue; 21 | } 22 | 23 | public BlockingQueue getOutputQueue() { 24 | return this.outputQueue; 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/SignalCarrier.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class SignalCarrier { 4 | 5 | public void doWait() throws InterruptedException{ 6 | synchronized(this) { 7 | System.out.println(Thread.currentThread().getName() + " calling wait()"); 8 | this.wait(); 9 | System.out.println(Thread.currentThread().getName() + " exited wait()"); 10 | } 11 | } 12 | 13 | public void doNotify() { 14 | synchronized (this) { 15 | System.out.println(Thread.currentThread().getName() + " calling notify()"); 16 | this.notify(); 17 | System.out.println(Thread.currentThread().getName() + " exited notify()"); 18 | } 19 | } 20 | 21 | public void doNotifyAll() { 22 | synchronized (this) { 23 | System.out.println(Thread.currentThread().getName() + " calling notify()"); 24 | this.notifyAll(); 25 | System.out.println(Thread.currentThread().getName() + " exited notify()"); 26 | } 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/SignalCounter.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class SignalCounter { 4 | 5 | private int signals = 0; 6 | 7 | public void doNotify() { 8 | synchronized (this) { 9 | this.signals++; 10 | System.out.println("Signals stored: " + this.signals); 11 | 12 | this.notify(); 13 | } 14 | } 15 | 16 | public void doWait() throws InterruptedException{ 17 | synchronized(this) { 18 | this.signals--; 19 | if(this.signals >= 0) { 20 | System.out.println(Thread.currentThread().getName() 21 | + " - " + (this.signals+1) + 22 | " signal(s) were stored. Exiting without wait()."); 23 | return; 24 | } 25 | System.out.println(Thread.currentThread().getName() + " calling wait()"); 26 | this.wait(); 27 | System.out.println(Thread.currentThread().getName() + " exited wait()"); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/SignalHolder.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class SignalHolder { 4 | 5 | private boolean signalRaised = false; 6 | private boolean isThreadWaiting = false; 7 | 8 | 9 | public void doNotify() { 10 | synchronized (this) { 11 | System.out.println(Thread.currentThread().getName() + " calling notify()"); 12 | if(!this.isThreadWaiting){ 13 | this.signalRaised = true; 14 | } 15 | this.notify(); 16 | System.out.println(Thread.currentThread().getName() + " exited notify()"); 17 | } 18 | } 19 | 20 | public void doWait() throws InterruptedException{ 21 | synchronized(this) { 22 | if(this.signalRaised) { 23 | System.out.println(Thread.currentThread().getName() + " signal was already raised - lowering signal and returning"); 24 | this.signalRaised = false; 25 | return; 26 | } 27 | System.out.println(Thread.currentThread().getName() + " calling wait()"); 28 | this.isThreadWaiting = true; 29 | this.wait(); 30 | this.isThreadWaiting = false; 31 | System.out.println(Thread.currentThread().getName() + " exited wait()"); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/SpuriousWakeupSignalGuard.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class SpuriousWakeupSignalGuard { 4 | 5 | Object myMonitorObject = new Object(); 6 | boolean wasSignalled = false; 7 | 8 | public void doNotify(){ 9 | synchronized(myMonitorObject){ 10 | wasSignalled = true; 11 | myMonitorObject.notify(); 12 | } 13 | } 14 | 15 | public void doWait() throws InterruptedException { 16 | synchronized(myMonitorObject){ 17 | while(!wasSignalled){ 18 | myMonitorObject.wait(); 19 | } 20 | //clear signal and continue running. 21 | wasSignalled = false; 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class ThreadSignalingExample { 4 | 5 | public static void main(String[] args) { 6 | 7 | SignalCarrier signalCarrier = new SignalCarrier(); 8 | 9 | Thread waiter = new Thread( () -> { 10 | try { 11 | signalCarrier.doWait(); 12 | } catch (InterruptedException e) { 13 | e.printStackTrace(); 14 | } 15 | }); 16 | 17 | Thread notifier = new Thread( () -> { 18 | signalCarrier.doNotify(); 19 | }); 20 | 21 | notifier.start(); 22 | waiter.start(); 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample2.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class ThreadSignalingExample2 { 4 | 5 | public static void main(String[] args) { 6 | 7 | SignalHolder signalHolder = new SignalHolder(); 8 | 9 | Thread waiter = new Thread( () -> { 10 | try { 11 | signalHolder.doWait(); 12 | } catch (InterruptedException e) { 13 | e.printStackTrace(); 14 | } 15 | }); 16 | 17 | 18 | Thread notifier = new Thread( () -> { 19 | signalHolder.doNotify(); 20 | }); 21 | 22 | 23 | waiter.start(); 24 | notifier.start(); 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample3.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class ThreadSignalingExample3 { 4 | 5 | public static void main(String[] args) { 6 | 7 | SignalCounter signalCounter = new SignalCounter(); 8 | 9 | Thread waiter = new Thread( () -> { 10 | for(int i=0; i<100; i++) { 11 | try { 12 | signalCounter.doWait(); 13 | } catch (InterruptedException e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | }); 18 | 19 | Thread notifier = new Thread( () -> { 20 | for(int i=0; i<100; i++) { 21 | signalCounter.doNotify(); 22 | } 23 | }); 24 | 25 | notifier.start(); 26 | waiter.start(); 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExample4.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | import java.util.concurrent.Semaphore; 4 | 5 | public class ThreadSignalingExample4 { 6 | 7 | public static void main(String[] args) { 8 | 9 | Object signalObject = new Object(); 10 | 11 | Thread waiter = new Thread( () -> { 12 | synchronized(signalObject) { 13 | try { 14 | signalObject.wait(); 15 | } catch (InterruptedException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | }); 20 | 21 | Thread notifier = new Thread( () -> { 22 | synchronized (signalObject) { 23 | signalObject.notifyAll(); 24 | } 25 | }); 26 | 27 | notifier.start(); 28 | waiter.start(); 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/threadsignaling/ThreadSignalingExampleBasic.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.threadsignaling; 2 | 3 | public class ThreadSignalingExampleBasic { 4 | 5 | public static void main(String[] args) { 6 | 7 | Object signalObject = new Object(); 8 | 9 | Thread waitingThread = new Thread( () -> { 10 | synchronized(signalObject) { 11 | try { 12 | signalObject.wait(); 13 | 14 | } catch (InterruptedException e) { 15 | e.printStackTrace(); 16 | } 17 | } 18 | }); 19 | 20 | Thread notifyingThread = new Thread( () -> { 21 | synchronized(signalObject) { 22 | signalObject.notify(); 23 | } 24 | }); 25 | 26 | waitingThread.start(); 27 | notifyingThread.start(); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/virtualthreads/ExecutorServiceVirtualThreadsExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.virtualthreads; 2 | 3 | import java.util.concurrent.*; 4 | 5 | public class ExecutorServiceVirtualThreadsExample { 6 | 7 | public static void main(String[] args) { 8 | 9 | ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); 10 | 11 | executor.submit(() -> { 12 | System.out.println("This is a Runnable that is executed by a virtual thread"); 13 | }); 14 | 15 | Callable callable = new Callable<>() { 16 | @Override 17 | public String call() throws Exception { 18 | System.out.println("Callable executed by virtual thread"); 19 | return "Result from Callable"; 20 | } 21 | }; 22 | 23 | Future futureResult = executor.submit(callable); 24 | 25 | try { 26 | System.out.println(futureResult.get()); 27 | } catch (InterruptedException e) { 28 | throw new RuntimeException(e); 29 | } catch (ExecutionException e) { 30 | throw new RuntimeException(e); 31 | } 32 | 33 | executor.shutdown(); 34 | try { 35 | executor.awaitTermination(10, TimeUnit.SECONDS); 36 | } catch (InterruptedException e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/virtualthreads/VirtualThreadExample.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.virtualthreads; 2 | 3 | public class VirtualThreadExample { 4 | 5 | public static void main(String[] args) { 6 | 7 | // Example 1: Create Runnable. Create and start virtual thread 8 | Runnable runnable = () -> { 9 | for(int i=0; i<10; i++) { 10 | System.out.println("Index: " + i); 11 | } 12 | }; 13 | 14 | Thread vThread1 = Thread.ofVirtual().start(runnable); 15 | 16 | 17 | // Example 2: Create but do not start virtual thread 18 | Thread vThreadUnstarted = Thread.ofVirtual().unstarted(runnable); 19 | 20 | 21 | 22 | vThreadUnstarted.start(); 23 | 24 | // Example 4: How to join a virtual thread 25 | try { 26 | vThreadUnstarted.join(); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/concurrency/virtualthreads/VirtualThreadExample2.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.concurrency.virtualthreads; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class VirtualThreadExample2 { 7 | 8 | public static void main(String[] args) { 9 | 10 | List vThreads = new ArrayList<>(); 11 | 12 | int vThreadCount = 100_000; 13 | 14 | for(int i=0; i { 17 | int result = 1; 18 | for(int j=0; j<10; j++) { 19 | result *= (j + 1); 20 | } 21 | System.out.println("Result[" + vThreadIndex +"]: " + result); 22 | }); 23 | 24 | vThreads.add(vThread); 25 | } 26 | 27 | for(int i=0; i cars = new ArrayList<>(); 9 | //cars.add(new Truck(3000, 800, 200, 5000)); 10 | 11 | cars.add(new Car(1000, 400, 180, 5)); 12 | cars.add(new Car(1200, 450, 182, 5)); 13 | cars.add(new Car(1500, 480, 150, 7)); 14 | 15 | int passengerCapacitySum = sumPassengerCapacityGenerics(cars); 16 | System.out.println("total passenger capacity: " + passengerCapacitySum); 17 | } 18 | 19 | public static int sumPassengerCapacity(List cars) { 20 | int passengerCapacitySum = 0; 21 | for(int i=0; i cars) { 29 | int passengerCapacitySum = 0; 30 | for(int i=0; i cars) { 47 | int passengerCapacitySum = 0; 48 | for(Car car : cars){ 49 | passengerCapacitySum += car.getPassengerCapacity(); 50 | } 51 | return passengerCapacitySum; 52 | } 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/generics/JavaGenericsExample2.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.generics; 2 | 3 | import java.util.*; 4 | 5 | public class JavaGenericsExample2 { 6 | 7 | public static void main(String[] args) { 8 | List carList = new ArrayList<>(); 9 | Set carSet = new HashSet<>(); 10 | Map carMap = new HashMap<>(); 11 | 12 | for(Car car : carList) { 13 | 14 | } 15 | 16 | for(Car car: carSet) { 17 | 18 | } 19 | 20 | for(String key: carMap.keySet()){ 21 | 22 | } 23 | 24 | for(Car car: carMap.values()) { 25 | 26 | } 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/generics/Truck.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.generics; 2 | 3 | public class Truck extends Vehicle { 4 | 5 | protected int loadCapacity = 0; 6 | 7 | public Truck(int weight, int length, int width, int loadCapacity) { 8 | super(weight, length, width); 9 | this.loadCapacity = loadCapacity; 10 | } 11 | 12 | public int getLoadCapacity() { 13 | return loadCapacity; 14 | } 15 | 16 | public void setLoadCapacity(int loadCapacity) { 17 | this.loadCapacity = loadCapacity; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/generics/Vehicle.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.generics; 2 | 3 | public class Vehicle { 4 | 5 | protected int weight; 6 | protected int length; 7 | protected int width; 8 | 9 | 10 | public Vehicle() {} 11 | 12 | public Vehicle(int weight, int length, int width) { 13 | this.weight = weight; 14 | this.length = length; 15 | this.width = width; 16 | } 17 | 18 | public int getWeight() { 19 | return weight; 20 | } 21 | 22 | public void setWeight(int weight) { 23 | this.weight = weight; 24 | } 25 | 26 | public int getLength() { 27 | return length; 28 | } 29 | 30 | public void setLength(int length) { 31 | this.length = length; 32 | } 33 | 34 | public int getWidth() { 35 | return width; 36 | } 37 | 38 | public void setWidth(int width) { 39 | this.width = width; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/jenkov/java/switchexp/SwitchExamples.java: -------------------------------------------------------------------------------- 1 | package com.jenkov.java.switchexp; 2 | 3 | public class SwitchExamples { 4 | 5 | public static void main(String[] args) { 6 | 7 | int val = Integer.parseInt("123"); 8 | 9 | switch(val) { 10 | case 123 : { 11 | System.out.println("123"); 12 | break; 13 | } 14 | 15 | default: { 16 | System.out.println("Default"); 17 | } 18 | } 19 | 20 | String result = switch(val) { 21 | case 123 -> "123-1234"; 22 | 23 | default -> "Default-Default"; 24 | }; 25 | 26 | System.out.println(result); 27 | 28 | } 29 | 30 | public static void switchOnType() { 31 | 32 | Integer input = Integer.valueOf(123); 33 | 34 | String result = 35 | switch(input) { 36 | //case Integer.class -> "Integer"; 37 | case 123 -> "Low number"; 38 | default -> "Unknown"; 39 | }; 40 | } 41 | } 42 | --------------------------------------------------------------------------------