├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── balarawool │ └── continuations │ ├── ContinuationsDemo.java │ ├── GeneratorDemo.java │ ├── GeneratorDemo2.java │ ├── ThreadExamples.java │ ├── platformthread │ ├── Demo.java │ ├── Scheduler.java │ └── WaitingOperation.java │ └── virtualthread │ ├── Demo.java │ ├── VirtualThread.java │ ├── VirtualThreadScheduler.java │ └── WaitingOperation.java └── test └── java └── com └── balarawool └── continuations └── TestThreadExamples.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | /.idea/ 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Continuations 2 | 3 | This repo contains source code for my talk "Continuations - The magic behind virtual threads in Java". 4 | 5 | The talk discusses continuations and how they are used to implement virtual threads in Java. 6 | If you have questions, suggestions to improve or any other feedback, please reach me out at [@BalaRawool](https://twitter.com/BalaRawool) 7 | 8 | ## Prerequisites 9 | Make sure 10 | - you use JDK 24 or higher 11 | - you compile and run these classes with these VM arguments: `--enable-preview --add-exports java.base/jdk.internal.vm=ALL-UNNAMED` 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.balarawool.vtdemo 8 | Continuations 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 24 13 | 24 14 | UTF-8 15 | 16 | 17 | 18 | 19 | junit 20 | junit 21 | 4.13.2 22 | test 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-api 27 | 5.8.2 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 37 | 24 38 | 24 39 | --enable-preview 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/ContinuationsDemo.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations; 2 | 3 | import jdk.internal.vm.Continuation; 4 | import jdk.internal.vm.ContinuationScope; 5 | 6 | public class ContinuationsDemo { 7 | public static void main(String[] args) { 8 | var cont = getContinuation(); 9 | cont.run(); 10 | System.out.println("Do something"); 11 | cont.run(); 12 | cont.run(); 13 | } 14 | 15 | private static Continuation getContinuation() { 16 | var scope = new ContinuationScope("Demo"); 17 | var cont = new Continuation(scope, () -> { 18 | System.out.println("A"); 19 | Continuation.yield(scope); 20 | System.out.println("B"); 21 | Continuation.yield(scope); 22 | System.out.println("C"); 23 | }); 24 | return cont; 25 | } 26 | 27 | private static Runnable getRunnable() { 28 | Runnable runnable = () -> { 29 | System.out.println("A"); 30 | System.out.println("B"); 31 | System.out.println("C"); 32 | }; 33 | return runnable; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/GeneratorDemo.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations; 2 | 3 | import jdk.internal.vm.Continuation; 4 | import jdk.internal.vm.ContinuationScope; 5 | 6 | import java.util.function.Consumer; 7 | 8 | public class GeneratorDemo { 9 | public static void main(String[] args) { 10 | var generator = new Generator(source -> { 11 | source.yield("A"); 12 | source.yield("B"); 13 | source.yield("C"); 14 | }); 15 | 16 | while (generator.hasNext()) { 17 | System.out.println(generator.next()); 18 | System.out.println("Do something"); 19 | } 20 | } 21 | 22 | public static class Generator { 23 | private ContinuationScope scope; 24 | private Continuation cont; 25 | private Source source; 26 | 27 | public boolean hasNext() { 28 | return !cont.isDone(); 29 | } 30 | 31 | public T next() { 32 | var t = source.getValue(); 33 | cont.run(); 34 | return t; 35 | } 36 | 37 | public class Source { 38 | private T value; 39 | 40 | public void yield(T t) { 41 | value = t; 42 | Continuation.yield(scope); 43 | } 44 | 45 | private T getValue() { 46 | return value; 47 | } 48 | } 49 | 50 | public Generator(Consumer consumer) { 51 | scope = new ContinuationScope("Generator"); 52 | source = new Source(); 53 | cont = new Continuation(scope, () -> consumer.accept(source)); 54 | cont.run(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/GeneratorDemo2.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations; 2 | 3 | import jdk.internal.vm.Continuation; 4 | import jdk.internal.vm.ContinuationScope; 5 | 6 | import java.util.function.Consumer; 7 | 8 | /** 9 | * This class is slight improvement over GeneratorDemo. 10 | * There the Generator proactively calculates the next value and stores it. 11 | * Here Generator only calculates the next value when it is asked for. 12 | */ 13 | public class GeneratorDemo2 { 14 | public static void main(String[] args) { 15 | // Define a Generator 16 | var gen = new Generator(source -> { 17 | System.out.println("Complex calculation for A"); 18 | source.yield("A"); 19 | System.out.println("Complex calculation for B"); 20 | source.yield("B"); 21 | System.out.println("Complex calculation for C"); 22 | source.yieldAndReturn("C"); 23 | }); 24 | 25 | // Use Generator 26 | while(gen.hasNext()) { 27 | System.out.println("Getting next value..."); 28 | System.out.println(gen.next()); 29 | } 30 | } 31 | 32 | public static class Generator { 33 | private ContinuationScope scope; 34 | private Continuation cont; 35 | private Source source; 36 | 37 | public boolean hasNext() { 38 | return !cont.isDone(); 39 | } 40 | 41 | public T next() { 42 | cont.run(); 43 | return source.getValue(); 44 | } 45 | 46 | public class Source { 47 | private T value; 48 | 49 | public void yield(T t) { 50 | value = t; 51 | Continuation.yield(scope); 52 | } 53 | 54 | private T getValue() { 55 | return value; 56 | } 57 | 58 | public void yieldAndReturn(T t) { 59 | value = t; 60 | } 61 | } 62 | 63 | public Generator(Consumer consumer) { 64 | scope = new ContinuationScope("Generator"); 65 | source = new Source(); 66 | cont = new Continuation(scope, () -> { consumer.accept(source);}); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/ThreadExamples.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations; 2 | 3 | import java.util.concurrent.Executors; 4 | 5 | public class ThreadExamples { 6 | public static void threads() { 7 | try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { 8 | executor.submit(() -> System.out.println(Thread.currentThread())); 9 | executor.submit(() -> System.out.println(Thread.currentThread())); 10 | executor.submit(() -> System.out.println(Thread.currentThread())); 11 | executor.submit(() -> System.out.println(Thread.currentThread())); 12 | executor.submit(() -> System.out.println(Thread.currentThread())); 13 | executor.submit(() -> System.out.println(Thread.currentThread())); 14 | executor.submit(() -> System.out.println(Thread.currentThread())); 15 | executor.submit(() -> System.out.println(Thread.currentThread())); 16 | executor.submit(() -> System.out.println(Thread.currentThread())); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/platformthread/Demo.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.platformthread; 2 | 3 | public class Demo { 4 | public static final Scheduler SCHEDULER = new Scheduler(); 5 | 6 | public static void main(String[] args) { 7 | for (int i = 0; i < 1000; i++) { 8 | 9 | Runnable r1 = () -> { 10 | System.out.println("1.1"); 11 | System.out.println("1.2"); 12 | WaitingOperation.perform("Network", 2); 13 | System.out.println("1.3"); 14 | System.out.println("1.4"); 15 | }; 16 | Runnable r2 = () -> { 17 | System.out.println("2.1"); 18 | System.out.println("2.2"); 19 | WaitingOperation.perform("DB", 5); 20 | System.out.println("2.3"); 21 | System.out.println("2.4"); 22 | }; 23 | SCHEDULER.schedule(r1); 24 | SCHEDULER.schedule(r2); 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/platformthread/Scheduler.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.platformthread; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | public class Scheduler { 7 | private ExecutorService executor = Executors.newFixedThreadPool(10); 8 | public void schedule(Runnable runnable) { 9 | executor.submit(runnable); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/platformthread/WaitingOperation.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.platformthread; 2 | 3 | public class WaitingOperation { 4 | public static void perform(String name, int delay) { 5 | System.out.println("Thread: "+Thread.currentThread()+" Waiting for "+name+" for "+delay+" seconds"); 6 | try { 7 | Thread.sleep(delay * 1_000L); 8 | } catch (InterruptedException e) { 9 | throw new RuntimeException(e); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/virtualthread/Demo.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.virtualthread; 2 | 3 | public class Demo { 4 | public static final VirtualThreadScheduler SCHEDULER = new VirtualThreadScheduler(); 5 | 6 | public static void main(String[] args) { 7 | new Thread(SCHEDULER::start).start(); 8 | 9 | for (int i = 0; i < 1000; i++) { 10 | var vt1 = new VirtualThread(() -> { 11 | System.out.println("1.1"); 12 | System.out.println("1.2"); 13 | WaitingOperation.perform("Network", 2); 14 | System.out.println("1.3"); 15 | System.out.println("1.4"); 16 | }); 17 | var vt2 = new VirtualThread(() -> { 18 | System.out.println("2.1"); 19 | System.out.println("2.2"); 20 | WaitingOperation.perform("DB", 5); 21 | System.out.println("2.3"); 22 | System.out.println("2.4"); 23 | }); 24 | 25 | SCHEDULER.schedule(vt1); 26 | SCHEDULER.schedule(vt2); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/virtualthread/VirtualThread.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.virtualthread; 2 | 3 | import jdk.internal.vm.Continuation; 4 | import jdk.internal.vm.ContinuationScope; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | public class VirtualThread { 9 | private static final AtomicInteger COUNTER = new AtomicInteger(1); 10 | public static final ContinuationScope SCOPE = new ContinuationScope("VirtualThread"); 11 | 12 | private Continuation cont; 13 | private int id; 14 | 15 | public VirtualThread(Runnable runnable) { 16 | id = COUNTER.getAndIncrement(); 17 | cont = new Continuation(SCOPE, runnable); 18 | } 19 | 20 | public void run() { 21 | System.out.println("VirtualThread "+id+" is running on "+Thread.currentThread()); 22 | cont.run(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/virtualthread/VirtualThreadScheduler.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.virtualthread; 2 | 3 | import java.util.Queue; 4 | import java.util.concurrent.ConcurrentLinkedQueue; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | 8 | public class VirtualThreadScheduler { 9 | public static ScopedValue CURRENT_VIRTUAL_THREAD = ScopedValue.newInstance(); 10 | 11 | private Queue queue = new ConcurrentLinkedQueue<>(); 12 | private ExecutorService executor = Executors.newFixedThreadPool(10); 13 | 14 | public void start() { 15 | while (true) { 16 | if (!queue.isEmpty()) { 17 | var vt = queue.remove(); 18 | executor.submit(() -> ScopedValue.where(CURRENT_VIRTUAL_THREAD, vt) 19 | .run(vt::run)); 20 | } 21 | } 22 | } 23 | 24 | public void schedule(VirtualThread virtualThread) { 25 | queue.add(virtualThread); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/balarawool/continuations/virtualthread/WaitingOperation.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations.virtualthread; 2 | 3 | import jdk.internal.vm.Continuation; 4 | 5 | import java.util.Timer; 6 | import java.util.TimerTask; 7 | 8 | import static com.balarawool.continuations.virtualthread.Demo.SCHEDULER; 9 | import static com.balarawool.continuations.virtualthread.VirtualThread.SCOPE; 10 | import static com.balarawool.continuations.virtualthread.VirtualThreadScheduler.CURRENT_VIRTUAL_THREAD; 11 | 12 | public class WaitingOperation { 13 | 14 | public static void perform(String name, int duration) { 15 | var virtualThread = CURRENT_VIRTUAL_THREAD.get(); 16 | System.out.println("Waiting for "+name+" for "+duration+" seconds"); 17 | 18 | var timer = new Timer(); 19 | timer.schedule(new TimerTask() { 20 | @Override 21 | public void run() { 22 | SCHEDULER.schedule(virtualThread); 23 | timer.cancel(); 24 | } 25 | }, duration * 1_000L); 26 | Continuation.yield(SCOPE); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/balarawool/continuations/TestThreadExamples.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.continuations; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | public class TestThreadExamples { 6 | @Test 7 | public void threads() { 8 | ThreadExamples.threads(); 9 | } 10 | } --------------------------------------------------------------------------------