├── .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 | }
--------------------------------------------------------------------------------