├── .doc
└── java-virtual-thread.png
├── .gitignore
├── 01-virtual-thread-playground
└── virtual-thread-playground
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── vinsguru
│ ├── sec01
│ ├── InboundOutboundTaskDemo.java
│ └── Task.java
│ ├── sec02
│ ├── StackTraceDemo.java
│ └── Task.java
│ ├── sec03
│ ├── CPUTaskDemo.java
│ └── Task.java
│ ├── sec04
│ └── CooperativeSchedulingDemo.java
│ ├── sec05
│ ├── Lec01RaceCondition.java
│ ├── Lec02Synchronization.java
│ ├── Lec03SynchronizationWithIO.java
│ ├── Lec04ReentrantLock.java
│ └── Lec05ReentrantLockWithIO.java
│ ├── sec06
│ ├── Lec01ThreadFactory.java
│ └── Lec02ThreadMethodsDemo.java
│ ├── sec07
│ ├── Lec01AutoCloseable.java
│ ├── Lec02ExecutorServiceTypes.java
│ ├── Lec03AccessResponseUsingFuture.java
│ ├── Lec04AggregatorDemo.java
│ ├── Lec05ConcurrencyLimit.java
│ ├── Lec06ConcurrencyLimitWithSemaphore.java
│ ├── Lec07ScheduledExecutorWithVirtualThreads.java
│ ├── aggregator
│ │ ├── AggregatorService.java
│ │ └── ProductDto.java
│ ├── concurrencylimit
│ │ └── ConcurrencyLimiter.java
│ └── externalservice
│ │ └── Client.java
│ ├── sec08
│ ├── Lec01SimpleCompletableFuture.java
│ ├── Lec02RunAsync.java
│ ├── Lec03SupplyAsync.java
│ ├── Lec04GetProducts.java
│ ├── Lec05AggregatorDemo.java
│ ├── Lec06AllOf.java
│ ├── Lec07AnyOf.java
│ ├── Lec08ThenCombine.java
│ ├── aggregator
│ │ ├── AggregatorService.java
│ │ └── ProductDto.java
│ └── externalservice
│ │ └── Client.java
│ ├── sec09
│ ├── Lec01ThreadLocal.java
│ ├── Lec02ThreadLocal.java
│ ├── Lec03ScopedValues.java
│ ├── Lec04SuccessOrFailure.java
│ ├── Lec05CancelOnFailure.java
│ ├── Lec06FirstSuccess.java
│ ├── Lec07ScopedValuesWithStructuredTaskScope.java
│ └── MapMulti.java
│ └── util
│ └── CommonUtils.java
├── 02-external-services
└── external-services-v1.jar
├── 03-trip-advisor-application
└── trip-advisor
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── vinsguru
│ │ │ └── tripadvisor
│ │ │ ├── TripAdvisorApplication.java
│ │ │ ├── client
│ │ │ ├── AccommodationServiceClient.java
│ │ │ ├── EventServiceClient.java
│ │ │ ├── FlightReservationServiceClient.java
│ │ │ ├── FlightSearchServiceClient.java
│ │ │ ├── LocalRecommendationServiceClient.java
│ │ │ ├── TransportationServiceClient.java
│ │ │ └── WeatherServiceClient.java
│ │ │ ├── config
│ │ │ ├── ExecutorServiceConfig.java
│ │ │ └── ServiceClientsConfig.java
│ │ │ ├── controller
│ │ │ └── TripController.java
│ │ │ ├── dto
│ │ │ ├── Accommodation.java
│ │ │ ├── CarRental.java
│ │ │ ├── Event.java
│ │ │ ├── Flight.java
│ │ │ ├── FlightReservationRequest.java
│ │ │ ├── FlightReservationResponse.java
│ │ │ ├── LocalRecommendations.java
│ │ │ ├── PublicTransportation.java
│ │ │ ├── Transportation.java
│ │ │ ├── TripPlan.java
│ │ │ ├── TripReservationRequest.java
│ │ │ └── Weather.java
│ │ │ └── service
│ │ │ ├── TripPlanService.java
│ │ │ └── TripReservationService.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── vinsguru
│ └── tripadvisor
│ └── RestClientTests.java
├── 04-jmeter-test-scripts
├── trip-plan.jmx
└── trip-reserve.jmx
└── README.md
/.doc/java-virtual-thread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinsguru/java-virtual-thread-course/a808dcc4936f955837fad6c3400ad757838049fc/.doc/java-virtual-thread.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Java template
2 | # Compiled class file
3 | **/*.class
4 |
5 | # Log file
6 | **/*.log
7 |
8 | # BlueJ files
9 | **/*.ctxt
10 |
11 | # Mobile Tools for Java (J2ME)
12 | **/.mtj.tmp/
13 |
14 | # Package Files #
15 | **/*.jar
16 | !**/external-services-v1.jar
17 | **/*.war
18 | **/*.nar
19 | **/*.ear
20 | **/*.zip
21 | **/*.tar.gz
22 | **/*.rar
23 |
24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
25 | **/hs_err_pid*
26 |
27 | ### Maven template
28 | **/target/
29 | **/pom.xml.tag
30 | **/pom.xml.releaseBackup
31 | **/pom.xml.versionsBackup
32 | **/pom.xml.next
33 | **/release.properties
34 | **/dependency-reduced-pom.xml
35 | **/buildNumber.properties
36 | **/.mvn/timing.properties
37 | **/.mvn/wrapper/maven-wrapper.jar
38 |
39 | **/*.iml
40 |
41 | **/.idea/
42 |
43 |
44 | **/HELP.md
45 | **/.mvn/
46 | **/mvnw
47 | **/mvnw.cmd
48 | **/node_modules/
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/.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
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.vinsguru
8 | virtual-thread-playground
9 | 1.0-SNAPSHOT
10 |
11 |
12 | UTF-8
13 | 1.5.12
14 |
15 |
16 |
17 |
18 | ch.qos.logback
19 | logback-classic
20 | ${logback.version}
21 |
22 |
23 |
24 |
25 |
26 | org.apache.maven.plugins
27 | maven-compiler-plugin
28 |
29 | 21
30 | 21
31 | --enable-preview
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec01/InboundOutboundTaskDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec01;
2 |
3 | import java.util.concurrent.CountDownLatch;
4 |
5 | /*
6 | To demo some blocking operations with both platform and virtual threads
7 | */
8 | public class InboundOutboundTaskDemo {
9 |
10 | private static final int MAX_PLATFORM = 10_000;
11 | private static final int MAX_VIRTUAL = 20;
12 |
13 | public static void main(String[] args) throws InterruptedException {
14 | platformThreadDemo1();
15 | // virtualThreadDemo();
16 | }
17 |
18 | /*
19 | To create a simple java platform thread
20 | */
21 | private static void platformThreadDemo1(){
22 | for (int i = 0; i < MAX_PLATFORM; i++) {
23 | int j = i;
24 | Thread thread = new Thread(() -> Task.ioIntensive(j));
25 | thread.start();
26 | }
27 | }
28 |
29 | /*
30 | To create platform thread using Thread.Builder
31 | */
32 | private static void platformThreadDemo2(){
33 | var builder = Thread.ofPlatform().name("vins", 1);
34 | for (int i = 0; i < MAX_PLATFORM; i++) {
35 | int j = i;
36 | Thread thread = builder.unstarted(() -> Task.ioIntensive(j));
37 | thread.start();
38 | }
39 | }
40 |
41 | /*
42 | To create platform daemon thread using Thread.Builder
43 | */
44 | private static void platformThreadDemo3() throws InterruptedException {
45 | var latch = new CountDownLatch(MAX_PLATFORM);
46 | var builder = Thread.ofPlatform().daemon().name("daemon", 1);
47 | for (int i = 0; i < MAX_PLATFORM; i++) {
48 | int j = i;
49 | Thread thread = builder.unstarted(() -> {
50 | Task.ioIntensive(j);
51 | latch.countDown();
52 | });
53 | thread.start();
54 | }
55 | latch.await();
56 | }
57 |
58 | /*
59 | To create virtual thread using Thread.Builder
60 | - virtual threads are daemon by default
61 | - virtual threads do not have any default name
62 | */
63 | private static void virtualThreadDemo() throws InterruptedException {
64 | var latch = new CountDownLatch(MAX_VIRTUAL);
65 | var builder = Thread.ofVirtual().name("virtual-", 1);
66 | for (int i = 0; i < MAX_VIRTUAL; i++) {
67 | int j = i;
68 | Thread thread = builder.unstarted(() -> {
69 | Task.ioIntensive(j);
70 | latch.countDown();
71 | });
72 | thread.start();
73 | }
74 | latch.await();
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec01/Task.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec01;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.time.Duration;
7 |
8 | public class Task {
9 |
10 | private static final Logger log = LoggerFactory.getLogger(Task.class);
11 |
12 | public static void ioIntensive(int i){
13 |
14 | try {
15 | log.info("starting I/O task {}. Thread Info: {}", i, Thread.currentThread());
16 | Thread.sleep(Duration.ofSeconds(10));
17 | log.info("ending I/O task {}. Thread Info: {}", i, Thread.currentThread());
18 | } catch (InterruptedException e) {
19 | throw new RuntimeException(e);
20 | }
21 |
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec02/StackTraceDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec02;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 |
5 | import java.time.Duration;
6 |
7 | public class StackTraceDemo {
8 |
9 | public static void main(String[] args) {
10 |
11 | demo(Thread.ofVirtual().name("virtual-", 1));
12 |
13 | CommonUtils.sleep(Duration.ofSeconds(2));
14 |
15 | }
16 |
17 | private static void demo(Thread.Builder builder){
18 | for (int i = 1; i <= 20 ; i++) {
19 | int j = i;
20 | builder.start(() -> Task.execute(j));
21 | }
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec02/Task.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec02;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 |
9 | /*
10 | Some chained method calls which could throw exceptions
11 | */
12 |
13 | public class Task {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Task.class);
16 |
17 | public static void execute(int i){
18 | log.info("starting task {}", i);
19 | try{
20 | method1(i);
21 | }catch (Exception e){
22 | log.error("error for {}", i, e);
23 | }
24 | log.info("ending task {}", i);
25 | }
26 |
27 | private static void method1(int i){
28 | CommonUtils.sleep(Duration.ofMillis(300));
29 | try{
30 | method2(i);
31 | }catch (Exception e){
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | private static void method2(int i){
37 | CommonUtils.sleep(Duration.ofMillis(100));
38 | method3(i);
39 | }
40 |
41 | private static void method3(int i){
42 | CommonUtils.sleep(Duration.ofMillis(500));
43 | if(i == 4){
44 | throw new IllegalArgumentException("i can not be 4");
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec03/CPUTaskDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec03;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.CountDownLatch;
8 |
9 | /*
10 | A simple demo to show that Virtual threads are NOT faster compared to Platform.
11 | */
12 | public class CPUTaskDemo {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(CPUTaskDemo.class);
15 | private static final int TASKS_COUNT = 3 * Runtime.getRuntime().availableProcessors();
16 |
17 | public static void main(String[] args) {
18 | log.info("Tasks Count: {}", TASKS_COUNT);
19 | for (int i = 0; i < 3; i++) {
20 | var totalTimeTaken = CommonUtils.timer(() -> demo(Thread.ofVirtual()));
21 | log.info("Total time taken with virtual {} ms", totalTimeTaken);
22 | totalTimeTaken = CommonUtils.timer(() -> demo(Thread.ofPlatform()));
23 | log.info("Total time taken with platform {} ms", totalTimeTaken);
24 | }
25 |
26 | }
27 |
28 | private static void demo(Thread.Builder builder){
29 | var latch = new CountDownLatch(TASKS_COUNT);
30 | for (int i = 1; i <= TASKS_COUNT; i++) {
31 | builder.start(() ->{
32 | Task.cpuIntensive(45);
33 | latch.countDown();
34 | });
35 | }
36 | try {
37 | latch.await();
38 | } catch (InterruptedException e) {
39 | throw new RuntimeException(e);
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec03/Task.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec03;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | /*
8 | A simple task to simulate a very time-consuming CPU intensive task
9 | */
10 | public class Task {
11 |
12 | private static final Logger log = LoggerFactory.getLogger(Task.class);
13 |
14 | public static void cpuIntensive(int i){
15 | log.info("starting CPU task. Thread Info: {}", Thread.currentThread());
16 | var timeTaken = CommonUtils.timer(() -> findFib(i));
17 | log.info("ending CPU task. time taken: {} ms", timeTaken);
18 | }
19 |
20 |
21 | // 2 ^ N algorithm - intentionally done this way to simulate CPU intensive task
22 | public static long findFib(long input){
23 | if(input < 2)
24 | return input;
25 | return findFib(input - 1) + findFib(input - 2);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec04/CooperativeSchedulingDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec04;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 |
9 | /*
10 | A simple demo to understand cooperative scheduling
11 | We will NOT have to use in an actual application
12 | */
13 |
14 | public class CooperativeSchedulingDemo {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(CooperativeSchedulingDemo.class);
17 |
18 | static {
19 | System.setProperty("jdk.virtualThreadScheduler.parallelism", "1");
20 | System.setProperty("jdk.virtualThreadScheduler.maxPoolSize", "1");
21 | }
22 |
23 | public static void main(String[] args) {
24 |
25 | var builder = Thread.ofVirtual();
26 | var t1 = builder.unstarted(() -> demo(1));
27 | var t2 = builder.unstarted(() -> demo(2));
28 | var t3 = builder.unstarted(() -> demo(3));
29 | t1.start();
30 | t2.start();
31 | t3.start();
32 | CommonUtils.sleep(Duration.ofSeconds(2));
33 |
34 | }
35 |
36 | private static void demo(int threadNumber){
37 | log.info("thread-{} started", threadNumber);
38 | for (int i = 0; i < 10; i++) {
39 | log.info("thread-{} is printing {}. Thread: {}", threadNumber, i, Thread.currentThread());
40 | Thread.yield(); // just for demo purposes
41 | }
42 | log.info("thread-{} ended", threadNumber);
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec05/Lec01RaceCondition.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec05;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /*
12 | Virtual Threads are indented for I/O tasks. This is a simple demo to show that race conditions are still applicable.
13 | */
14 |
15 | public class Lec01RaceCondition {
16 |
17 | private static final Logger log = LoggerFactory.getLogger(Lec01RaceCondition.class);
18 | private static final List list = new ArrayList<>();
19 |
20 | public static void main(String[] args) {
21 |
22 | demo(Thread.ofVirtual());
23 |
24 | CommonUtils.sleep(Duration.ofSeconds(2));
25 |
26 | log.info("list size: {}", list.size());
27 | }
28 |
29 | private static void demo(Thread.Builder builder){
30 | for (int i = 0; i < 50; i++) {
31 | builder.start(() -> {
32 | log.info("Task started. {}", Thread.currentThread());
33 | for (int j = 0; j < 200; j++) {
34 | inMemoryTask();
35 | }
36 | log.info("Task ended. {}", Thread.currentThread());
37 | });
38 | }
39 | }
40 |
41 | private static void inMemoryTask(){
42 | list.add(1);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec05/Lec02Synchronization.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec05;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /*
12 | Virtual Threads are indented for I/O tasks. This is a simple demo to show that race conditions are still applicable. How we normally fix it.
13 | */
14 | public class Lec02Synchronization {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec02Synchronization.class);
17 | private static final List list = new ArrayList<>();
18 |
19 | public static void main(String[] args) {
20 |
21 | demo(Thread.ofVirtual());
22 |
23 | CommonUtils.sleep(Duration.ofSeconds(2));
24 |
25 | log.info("list size: {}", list.size());
26 | }
27 |
28 | private static void demo(Thread.Builder builder){
29 | for (int i = 0; i < 50; i++) {
30 | builder.start(() -> {
31 | log.info("Task started. {}", Thread.currentThread());
32 | for (int j = 0; j < 200; j++) {
33 | inMemoryTask();
34 | }
35 | log.info("Task ended. {}", Thread.currentThread());
36 | });
37 | }
38 | }
39 |
40 | private static synchronized void inMemoryTask(){
41 | list.add(1);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec05/Lec03SynchronizationWithIO.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec05;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 |
9 | /*
10 | Demo: Virtual Thread Pinning
11 | */
12 | public class Lec03SynchronizationWithIO {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec03SynchronizationWithIO.class);
15 |
16 | // Use this to check if virtual threads are getting pinned in your application
17 | static {
18 | System.setProperty("jdk.tracePinnedThreads", "short");
19 | }
20 |
21 | public static void main(String[] args) {
22 |
23 | Runnable runnable = () -> log.info("*** Test Message ***");
24 |
25 | // We will not see this issue wit Platform threads
26 | // demo(Thread.ofPlatform());
27 | // Thread.ofPlatform().start(runnable);
28 |
29 | demo(Thread.ofVirtual());
30 | Thread.ofVirtual().start(runnable);
31 |
32 | CommonUtils.sleep(Duration.ofSeconds(15));
33 |
34 | }
35 |
36 | private static void demo(Thread.Builder builder){
37 | for (int i = 0; i < 50; i++) {
38 | builder.start(() -> {
39 | log.info("Task started. {}", Thread.currentThread());
40 | ioTask();
41 | log.info("Task ended. {}", Thread.currentThread());
42 | });
43 | }
44 | }
45 |
46 | private static synchronized void ioTask(){
47 | CommonUtils.sleep(Duration.ofSeconds(10));
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec05/Lec04ReentrantLock.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec05;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.concurrent.locks.Lock;
11 | import java.util.concurrent.locks.ReentrantLock;
12 |
13 | /*
14 | Virtual Threads are indented for I/O tasks. This is a simple demo the use of ReentrantLock
15 | */
16 | public class Lec04ReentrantLock {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(Lec04ReentrantLock.class);
19 | private static final Lock lock = new ReentrantLock();
20 | private static final List list = new ArrayList<>();
21 |
22 | public static void main(String[] args) {
23 |
24 | demo(Thread.ofVirtual());
25 |
26 | CommonUtils.sleep(Duration.ofSeconds(2));
27 |
28 | log.info("list size: {}", list.size());
29 | }
30 |
31 | private static void demo(Thread.Builder builder){
32 | for (int i = 0; i < 50; i++) {
33 | builder.start(() -> {
34 | log.info("Task started. {}", Thread.currentThread());
35 | for (int j = 0; j < 200; j++) {
36 | inMemoryTask();
37 | }
38 | log.info("Task ended. {}", Thread.currentThread());
39 | });
40 | }
41 | }
42 |
43 | private static void inMemoryTask(){
44 | try{
45 | lock.lock();
46 | list.add(1);
47 | }catch (Exception e){
48 | log.error("error", e);
49 | }finally {
50 | lock.unlock();
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec05/Lec05ReentrantLockWithIO.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec05;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.locks.Lock;
9 | import java.util.concurrent.locks.ReentrantLock;
10 |
11 | /*
12 | Virtual Threads are indented for I/O tasks.
13 | To avoid pinning with synchronized, use ReentrantLock
14 | */
15 | public class Lec05ReentrantLockWithIO {
16 |
17 | private static final Logger log = LoggerFactory.getLogger(Lec05ReentrantLockWithIO.class);
18 | private static final Lock lock = new ReentrantLock();
19 |
20 | static {
21 | System.setProperty("jdk.tracePinnedThreads", "short");
22 | }
23 |
24 | public static void main(String[] args) {
25 |
26 | Runnable runnable = () -> log.info("*** Test Message ***");
27 |
28 | demo(Thread.ofVirtual());
29 | Thread.ofVirtual().start(runnable);
30 |
31 | CommonUtils.sleep(Duration.ofSeconds(15));
32 |
33 | }
34 |
35 | private static void demo(Thread.Builder builder){
36 | for (int i = 0; i < 50; i++) {
37 | builder.start(() -> {
38 | log.info("Task started. {}", Thread.currentThread());
39 | ioTask();
40 | log.info("Task ended. {}", Thread.currentThread());
41 | });
42 | }
43 | }
44 |
45 | private static void ioTask(){
46 | try{
47 | lock.lock();
48 | CommonUtils.sleep(Duration.ofSeconds(10));
49 | }catch (Exception e){
50 | log.error("error", e);
51 | }finally {
52 | lock.unlock();
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec06/Lec01ThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec06;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.ThreadFactory;
9 |
10 | public class Lec01ThreadFactory {
11 |
12 | private static final Logger log = LoggerFactory.getLogger(Lec01ThreadFactory.class);
13 |
14 | public static void main(String[] args) {
15 |
16 | demo(Thread.ofVirtual().name("vins", 1).factory());
17 |
18 | CommonUtils.sleep(Duration.ofSeconds(3));
19 |
20 | }
21 |
22 | /*
23 | Create few threads
24 | Each thread creates 1 child thread
25 | It is a simple demo. In the real life, lets use ExecutorService etc
26 | Virtual threads are cheap to create.
27 | */
28 |
29 | private static void demo(ThreadFactory factory){
30 | for (int i = 0; i < 30; i++) {
31 | var t = factory.newThread(() -> {
32 | log.info("Task started. {}", Thread.currentThread());
33 | var ct = factory.newThread(() -> {
34 | log.info("Child task started. {}", Thread.currentThread());
35 | CommonUtils.sleep(Duration.ofSeconds(2));
36 | log.info("Child task ended. {}", Thread.currentThread());
37 | });
38 | ct.start();
39 | log.info("Task ended. {}", Thread.currentThread());
40 | });
41 | t.start();
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec06/Lec02ThreadMethodsDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec06;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 |
9 | /*
10 | Quick demo to show few useful thread methods
11 | */
12 | public class Lec02ThreadMethodsDemo {
13 |
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec02ThreadMethodsDemo.class);
16 |
17 | public static void main(String[] args) throws InterruptedException {
18 | join();
19 | CommonUtils.sleep(Duration.ofSeconds(1));
20 | }
21 |
22 | /*
23 | To check if a thread is virtual
24 | */
25 | private static void isVirtual() {
26 | var t1 = Thread.ofVirtual().start(() -> CommonUtils.sleep(Duration.ofSeconds(2)));
27 | var t2 = Thread.ofPlatform().start(() -> CommonUtils.sleep(Duration.ofSeconds(2)));
28 | log.info("Is t1 virtual: {}", t1.isVirtual());
29 | log.info("Is t2 virtual: {}", t2.isVirtual());
30 | log.info("Is current thread virtual: {}", Thread.currentThread().isVirtual());
31 | }
32 |
33 |
34 | /*
35 | To offload multiple time-consuming I/O calls to Virtual threads and wait for them to complete
36 | Note: We can do better in the actual application which we will develop later.
37 | It is a simple thread.join() demo
38 | */
39 | private static void join() throws InterruptedException {
40 | var t1 = Thread.ofVirtual().start(() -> {
41 | CommonUtils.sleep(Duration.ofSeconds(2));
42 | log.info("called product service");
43 | });
44 | var t2 = Thread.ofVirtual().start(() -> {
45 | CommonUtils.sleep(Duration.ofSeconds(2));
46 | log.info("called pricing service");
47 | });
48 | t1.join();
49 | t2.join();
50 | }
51 |
52 | /*
53 | To interrupt / stop the thread execution
54 | in some cases, java will throw interrupted exception, IO exception, socket exception etc
55 |
56 | We can also check if the current thread is interrupted
57 | Thread.currentThread().isInterrupted() - returns a boolean
58 |
59 | while(!Thread.currentThread().isInterrupted()){
60 | continue the work
61 | ...
62 | ...
63 | }
64 | */
65 | private static void interrupt() {
66 | var t1 = Thread.ofVirtual().start(() -> {
67 | CommonUtils.sleep(Duration.ofSeconds(2));
68 | log.info("called product service");
69 | });
70 | log.info("is t1 interrupted: {}", t1.isInterrupted());
71 | t1.interrupt();
72 | log.info("is t1 interrupted: {}", t1.isInterrupted());
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec01AutoCloseable.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.Executors;
9 |
10 | /*
11 | ExecutorService now extends the AutoCloseable
12 | */
13 | public class Lec01AutoCloseable {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec01AutoCloseable.class);
16 |
17 | public static void main(String[] args) {
18 |
19 | }
20 |
21 | // w/o autocloseable - we have to issue shutdown for short-lived applications
22 | private static void withoutAutoCloseable(){
23 | var executorService = Executors.newSingleThreadExecutor();
24 | executorService.submit(Lec01AutoCloseable::task);
25 | log.info("submitted");
26 | executorService.shutdown();
27 | }
28 |
29 | private static void withAutoCloseable(){
30 | try(var executorService = Executors.newSingleThreadExecutor()){
31 | executorService.submit(Lec01AutoCloseable::task);
32 | executorService.submit(Lec01AutoCloseable::task);
33 | executorService.submit(Lec01AutoCloseable::task);
34 | executorService.submit(Lec01AutoCloseable::task);
35 | log.info("submitted");
36 | }
37 | }
38 |
39 | private static void task(){
40 | CommonUtils.sleep(Duration.ofSeconds(1));
41 | log.info("task executed");
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec02ExecutorServiceTypes.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /*
13 | To discuss various executor service types
14 | */
15 | public class Lec02ExecutorServiceTypes {
16 |
17 | private static final Logger log = LoggerFactory.getLogger(Lec02ExecutorServiceTypes.class);
18 |
19 | public static void main(String[] args) {
20 |
21 | }
22 |
23 | // single thread executor - to execute tasks sequentially
24 | private static void single(){
25 | execute(Executors.newSingleThreadExecutor(), 3);
26 | }
27 |
28 | // fixed thread pool
29 | private static void fixed(){
30 | execute(Executors.newFixedThreadPool(5), 20);
31 | }
32 |
33 | // elastic thread pool
34 | private static void cached(){
35 | execute(Executors.newCachedThreadPool(), 200);
36 | }
37 |
38 | // ExecutorService which creates VirtualThread per task
39 | private static void virtual(){
40 | execute(Executors.newVirtualThreadPerTaskExecutor(), 10_000);
41 | }
42 |
43 | // To schedule tasks periodically
44 | private static void scheduled(){
45 | try(var executorService = Executors.newSingleThreadScheduledExecutor()){
46 | executorService.scheduleAtFixedRate(() -> {
47 | log.info("executing task");
48 | }, 0, 1, TimeUnit.SECONDS);
49 |
50 | CommonUtils.sleep(Duration.ofSeconds(5));
51 | }
52 | }
53 |
54 | private static void execute(ExecutorService executorService, int taskCount){
55 | try(executorService){
56 | for (int i = 0; i < taskCount; i++) {
57 | int j = i;
58 | executorService.submit(() -> ioTask(j));
59 | }
60 | log.info("submitted");
61 | }
62 | }
63 |
64 | private static void ioTask(int i){
65 | log.info("Task started: {}. Thread Info {}", i, Thread.currentThread());
66 | CommonUtils.sleep(Duration.ofSeconds(5));
67 | log.info("Task ended: {}. Thread Info {}", i, Thread.currentThread());
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec03AccessResponseUsingFuture.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.sec07.externalservice.Client;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.Executors;
8 |
9 | /*
10 | To get multiple products information in parallel
11 | */
12 | public class Lec03AccessResponseUsingFuture {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec03AccessResponseUsingFuture.class);
15 |
16 | public static void main(String[] args) throws Exception {
17 |
18 | try(var executor = Executors.newVirtualThreadPerTaskExecutor()){
19 |
20 | var product1 = executor.submit(() -> Client.getProduct(1));
21 | var product2 = executor.submit(() -> Client.getProduct(2));
22 | var product3 = executor.submit(() -> Client.getProduct(3));
23 |
24 | log.info("product-1: {}", product1.get());
25 | log.info("product-2: {}", product2.get());
26 | log.info("product-3: {}", product3.get());
27 |
28 | }
29 |
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec04AggregatorDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.sec07.aggregator.AggregatorService;
4 | import com.vinsguru.sec07.aggregator.ProductDto;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.util.concurrent.Executors;
9 | import java.util.concurrent.Future;
10 | import java.util.stream.IntStream;
11 |
12 | public class Lec04AggregatorDemo {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec04AggregatorDemo.class);
15 |
16 | public static void main(String[] args) throws Exception {
17 |
18 | // beans / singletons
19 | var executor = Executors.newVirtualThreadPerTaskExecutor();
20 | var aggregator = new AggregatorService(executor);
21 |
22 | var futures = IntStream.rangeClosed(1, 50)
23 | .mapToObj(id -> executor.submit(() -> aggregator.getProductDto(id))) .toList();
24 | var list = futures.stream()
25 | .map(Lec04AggregatorDemo::toProductDto)
26 | .toList();
27 |
28 | log.info("list: {}", list);
29 |
30 | }
31 |
32 | private static ProductDto toProductDto(Future future){
33 | try {
34 | return future.get();
35 | }catch (Exception e){
36 | throw new RuntimeException(e);
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec05ConcurrencyLimit.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.sec07.externalservice.Client;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.ExecutorService;
8 | import java.util.concurrent.Executors;
9 |
10 | public class Lec05ConcurrencyLimit {
11 |
12 | private static final Logger log = LoggerFactory.getLogger(Lec05ConcurrencyLimit.class);
13 |
14 | public static void main(String[] args) {
15 | // It is NOT supposed to be used this way as it pools Virtual Threads
16 | var factory = Thread.ofVirtual().name("vins", 1).factory();
17 | execute(Executors.newFixedThreadPool(3), 20);
18 | }
19 |
20 | private static void execute(ExecutorService executorService, int taskCount){
21 | try(executorService){
22 | for (int i = 1; i <= taskCount; i++) {
23 | int j = i;
24 | executorService.submit(() -> printProductInfo(j));
25 | }
26 | log.info("submitted");
27 | }
28 | }
29 |
30 | // 3rd party service
31 | // contract: 3 concurrent calls are allowed
32 | private static void printProductInfo(int id){
33 | log.info("{} => {}", id, Client.getProduct(id));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec06ConcurrencyLimitWithSemaphore.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.sec07.concurrencylimit.ConcurrencyLimiter;
4 | import com.vinsguru.sec07.externalservice.Client;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.util.concurrent.Executors;
9 |
10 | public class Lec06ConcurrencyLimitWithSemaphore {
11 |
12 | private static final Logger log = LoggerFactory.getLogger(Lec06ConcurrencyLimitWithSemaphore.class);
13 |
14 | public static void main(String[] args) throws Exception {
15 | var factory = Thread.ofVirtual().name("vins", 1).factory();
16 | var limiter = new ConcurrencyLimiter(Executors.newThreadPerTaskExecutor(factory), 3);
17 | execute(limiter, 200);
18 | }
19 |
20 | private static void execute(ConcurrencyLimiter concurrencyLimiter, int taskCount) throws Exception {
21 | try(concurrencyLimiter){
22 | for (int i = 1; i <= taskCount; i++) {
23 | int j = i;
24 | concurrencyLimiter.submit(() -> printProductInfo(j));
25 | }
26 | log.info("submitted");
27 | }
28 | }
29 |
30 | // 3rd party service
31 | // contract: 3 concurrent calls are allowed
32 | private static String printProductInfo(int id){
33 | var product = Client.getProduct(id);
34 | log.info("{} => {}", id, product);
35 | return product;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/Lec07ScheduledExecutorWithVirtualThreads.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07;
2 |
3 | import com.vinsguru.sec07.externalservice.Client;
4 | import com.vinsguru.util.CommonUtils;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.time.Duration;
9 | import java.util.concurrent.Executors;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | public class Lec07ScheduledExecutorWithVirtualThreads {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec07ScheduledExecutorWithVirtualThreads.class);
15 |
16 | public static void main(String[] args) {
17 | scheduled();
18 | }
19 |
20 | // To schedule tasks periodically
21 | private static void scheduled(){
22 | var scheduler = Executors.newSingleThreadScheduledExecutor();
23 | var executor = Executors.newVirtualThreadPerTaskExecutor();
24 | try(scheduler; executor){
25 | scheduler.scheduleAtFixedRate(() -> {
26 | executor.submit(() -> printProductInfo(1));
27 | }, 0, 3, TimeUnit.SECONDS);
28 |
29 | CommonUtils.sleep(Duration.ofSeconds(15));
30 | }
31 | }
32 |
33 | private static void printProductInfo(int id){
34 | log.info("{} => {}", id, Client.getProduct(id));
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/aggregator/AggregatorService.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07.aggregator;
2 |
3 | import com.vinsguru.sec07.externalservice.Client;
4 | import java.util.concurrent.ExecutorService;
5 |
6 | public class AggregatorService {
7 |
8 | private final ExecutorService executorService;
9 |
10 | public AggregatorService(ExecutorService executorService) {
11 | this.executorService = executorService;
12 | }
13 |
14 | public ProductDto getProductDto(int id) throws Exception {
15 | var product = executorService.submit(() -> Client.getProduct(id));
16 | var rating = executorService.submit(() -> Client.getRating(id));
17 | return new ProductDto(id, product.get(), rating.get());
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/aggregator/ProductDto.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07.aggregator;
2 |
3 | public record ProductDto(int id,
4 | String description,
5 | int rating) {
6 | }
7 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/concurrencylimit/ConcurrencyLimiter.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07.concurrencylimit;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.util.Queue;
7 | import java.util.concurrent.*;
8 |
9 | public class ConcurrencyLimiter implements AutoCloseable {
10 |
11 | private static final Logger log = LoggerFactory.getLogger(ConcurrencyLimiter.class);
12 |
13 | private final ExecutorService executor;
14 | private final Semaphore semaphore;
15 | private final Queue> queue;
16 |
17 | public ConcurrencyLimiter(ExecutorService executor, int limit) {
18 | this.executor = executor;
19 | this.semaphore = new Semaphore(limit);
20 | this.queue = new ConcurrentLinkedQueue<>();
21 | }
22 |
23 | public Future submit(Callable callable){
24 | this.queue.add(callable);
25 | return executor.submit(() -> executeTask() );
26 | }
27 |
28 | private T executeTask(){
29 | try{
30 | semaphore.acquire();
31 | return (T) this.queue.poll().call();
32 | }catch (Exception e){
33 | log.error("error", e);
34 | }finally {
35 | semaphore.release();
36 | }
37 | return null;
38 | }
39 |
40 | @Override
41 | public void close() throws Exception {
42 | this.executor.close();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec07/externalservice/Client.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec07.externalservice;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.net.URI;
7 |
8 | public class Client {
9 |
10 | private static final Logger log = LoggerFactory.getLogger(Client.class);
11 | private static final String PRODUCT_REQUEST_FORMAT = "http://localhost:7070/sec01/product/%d";
12 | private static final String RATING_REQUEST_FORMAT = "http://localhost:7070/sec01/rating/%d";
13 |
14 | public static String getProduct(int id){
15 | return callExternalService(PRODUCT_REQUEST_FORMAT.formatted(id));
16 | }
17 |
18 | public static Integer getRating(int id){
19 | return Integer.parseInt(
20 | callExternalService(RATING_REQUEST_FORMAT.formatted(id))
21 | );
22 | }
23 |
24 | private static String callExternalService(String url){
25 | log.info("calling {}", url);
26 | try(var stream = URI.create(url).toURL().openStream()){ // stream should be closed
27 | return new String(stream.readAllBytes()); // responsive size is small
28 | }catch (Exception e){
29 | throw new RuntimeException(e);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec01SimpleCompletableFuture.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.CompletableFuture;
9 |
10 | /*
11 | A very simple demo to show how CompletableFuture works
12 | Intentionally NOT using factory methods
13 | */
14 | public class Lec01SimpleCompletableFuture {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec01SimpleCompletableFuture.class);
17 |
18 | public static void main(String[] args) {
19 | log.info("main starts");
20 | var cf = slowTask();
21 | cf.thenAccept(v -> log.info("value={}", v));
22 |
23 | // log.info("value={}", cf.join());
24 | log.info("main ends");
25 |
26 | CommonUtils.sleep(Duration.ofSeconds(2));
27 | }
28 |
29 | private static CompletableFuture fastTask(){
30 | log.info("method starts");
31 | var cf = new CompletableFuture();
32 | cf.complete("hi");
33 | log.info("method ends");
34 | return cf;
35 | }
36 |
37 | private static CompletableFuture slowTask(){
38 | log.info("method starts");
39 | var cf = new CompletableFuture();
40 | Thread.ofVirtual().start(() -> {
41 | CommonUtils.sleep(Duration.ofSeconds(1));
42 | cf.complete("hi");
43 | });
44 | log.info("method ends");
45 | return cf;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec02RunAsync.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.Executors;
10 |
11 | /*
12 | Factory method
13 | Run async
14 | Executor
15 | */
16 | public class Lec02RunAsync {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(Lec02RunAsync.class);
19 |
20 | public static void main(String[] args) {
21 | log.info("main starts");
22 |
23 | runAsync()
24 | .thenRun(() -> log.info("it is done"))
25 | .exceptionally(ex -> {
26 | log.info("error - {}", ex.getMessage());
27 | return null;
28 | });
29 |
30 | log.info("main ends");
31 | CommonUtils.sleep(Duration.ofSeconds(2));
32 | }
33 |
34 | private static CompletableFuture runAsync(){
35 | log.info("method starts");
36 |
37 | var cf = CompletableFuture.runAsync(() -> {
38 | CommonUtils.sleep(Duration.ofSeconds(1));
39 | // log.info("task completed");
40 | throw new RuntimeException("oops");
41 | }, Executors.newVirtualThreadPerTaskExecutor());
42 |
43 | log.info("method ends");
44 | return cf;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec03SupplyAsync.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.Executors;
10 |
11 | /*
12 | We can supply values asynchronously
13 | Factory method
14 | Executor
15 | */
16 | public class Lec03SupplyAsync {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(Lec03SupplyAsync.class);
19 |
20 | public static void main(String[] args) {
21 | log.info("main starts");
22 | var cf = slowTask();
23 | cf.thenAccept(v -> log.info("value={}", v));
24 |
25 | // log.info("value={}", cf.join());
26 | log.info("main ends");
27 |
28 | CommonUtils.sleep(Duration.ofSeconds(2));
29 | }
30 |
31 | private static CompletableFuture slowTask(){
32 | log.info("method starts");
33 | var cf = CompletableFuture.supplyAsync(() -> {
34 | CommonUtils.sleep(Duration.ofSeconds(1));
35 | return "hi";
36 | }, Executors.newVirtualThreadPerTaskExecutor());
37 | log.info("method ends");
38 | return cf;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec04GetProducts.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.sec08.externalservice.Client;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.CompletableFuture;
8 | import java.util.concurrent.Executors;
9 |
10 | /*
11 | To get multiple products information in parallel
12 | */
13 | public class Lec04GetProducts {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec04GetProducts.class);
16 |
17 | public static void main(String[] args) throws Exception {
18 |
19 | try(var executor = Executors.newVirtualThreadPerTaskExecutor()){
20 |
21 | var product1 = CompletableFuture.supplyAsync(() -> Client.getProduct(1), executor);
22 | var product2 = CompletableFuture.supplyAsync(() -> Client.getProduct(2), executor);
23 | var product3 = CompletableFuture.supplyAsync(() -> Client.getProduct(3), executor);
24 |
25 | log.info("product-1: {}", product1.get());
26 | log.info("product-2: {}", product2.get());
27 | log.info("product-3: {}", product3.get());
28 |
29 | }
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec05AggregatorDemo.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.sec08.aggregator.AggregatorService;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.Executors;
8 |
9 | /*
10 | Aggregate and error handling
11 | */
12 | public class Lec05AggregatorDemo {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec05AggregatorDemo.class);
15 |
16 | public static void main(String[] args) throws Exception {
17 | // beans / singletons
18 | var executor = Executors.newVirtualThreadPerTaskExecutor();
19 | var aggregator = new AggregatorService(executor);
20 |
21 | log.info("product={}", aggregator.getProductDto(51));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec06AllOf.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.sec08.aggregator.AggregatorService;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.concurrent.CompletableFuture;
8 | import java.util.concurrent.Executors;
9 | import java.util.stream.IntStream;
10 |
11 | /*
12 | Demo the use of all-of
13 | */
14 | public class Lec06AllOf {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec06AllOf.class);
17 |
18 | public static void main(String[] args) {
19 |
20 | // beans / singletons
21 | var executor = Executors.newVirtualThreadPerTaskExecutor();
22 | var aggregator = new AggregatorService(executor);
23 |
24 | // create futures
25 | var futures = IntStream.rangeClosed(52, 100)
26 | .mapToObj(id -> CompletableFuture.supplyAsync(() -> aggregator.getProductDto(id), executor))
27 | .toList();
28 |
29 | // wait for all the completable-futures to complete
30 | CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
31 |
32 | var list = futures.stream()
33 | .map(CompletableFuture::join)
34 | .toList();
35 |
36 | log.info("list: {}", list);
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec07AnyOf.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.ExecutorService;
10 | import java.util.concurrent.Executors;
11 | import java.util.concurrent.ThreadLocalRandom;
12 |
13 | public class Lec07AnyOf {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec07AnyOf.class);
16 |
17 | public static void main(String[] args) {
18 |
19 | try(var executor = Executors.newVirtualThreadPerTaskExecutor()){
20 | var cf1 = getDeltaAirfare(executor);
21 | var cf2 = getFrontierAirfare(executor);
22 | log.info("airfare={}", CompletableFuture.anyOf(cf1, cf2).join());
23 | }
24 |
25 | }
26 |
27 | private static CompletableFuture getDeltaAirfare(ExecutorService executor){
28 | return CompletableFuture.supplyAsync(() -> {
29 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
30 | CommonUtils.sleep(Duration.ofMillis(random));
31 | return "Delta-$" + random;
32 | }, executor);
33 | }
34 |
35 | private static CompletableFuture getFrontierAirfare(ExecutorService executor){
36 | return CompletableFuture.supplyAsync(() -> {
37 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
38 | CommonUtils.sleep(Duration.ofMillis(random));
39 | return "Frontier-$" + random;
40 | }, executor);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/Lec08ThenCombine.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.ExecutorService;
10 | import java.util.concurrent.Executors;
11 | import java.util.concurrent.ThreadLocalRandom;
12 |
13 | public class Lec08ThenCombine {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec08ThenCombine.class);
16 | record Airfare(String airline, int amount) {}
17 |
18 | public static void main(String[] args) {
19 | try(var executor = Executors.newVirtualThreadPerTaskExecutor()){
20 | var cf1 = getDeltaAirfare(executor);
21 | var cf2 = getFrontierAirfare(executor);
22 | var bestDeal = cf1.thenCombine(cf2, (a, b) -> a.amount() <= b.amount() ? a : b)
23 | .thenApply(af -> new Airfare(af.airline(), (int) (af.amount() * 0.9)))
24 | .join();
25 |
26 | log.info("best deal={}", bestDeal);
27 | }
28 | }
29 |
30 | private static CompletableFuture getDeltaAirfare(ExecutorService executor){
31 | return CompletableFuture.supplyAsync(() -> {
32 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
33 | CommonUtils.sleep(Duration.ofMillis(random));
34 | log.info("Delta-{}", random);
35 | return new Airfare("Delta", random);
36 | }, executor);
37 | }
38 |
39 | private static CompletableFuture getFrontierAirfare(ExecutorService executor){
40 | return CompletableFuture.supplyAsync(() -> {
41 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
42 | CommonUtils.sleep(Duration.ofMillis(random));
43 | log.info("Frontier-{}", random);
44 | return new Airfare("Frontier", random);
45 | }, executor);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/aggregator/AggregatorService.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08.aggregator;
2 |
3 | import com.vinsguru.sec08.externalservice.Client;
4 |
5 | import java.util.concurrent.CompletableFuture;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | public class AggregatorService {
10 |
11 | private final ExecutorService executorService;
12 |
13 | public AggregatorService(ExecutorService executorService) {
14 | this.executorService = executorService;
15 | }
16 |
17 | public ProductDto getProductDto(int id) {
18 | var product = CompletableFuture.supplyAsync(() -> Client.getProduct(id), executorService)
19 | .orTimeout(1250, TimeUnit.MILLISECONDS)
20 | .exceptionally(ex -> null);
21 | var rating = CompletableFuture.supplyAsync(() -> Client.getRating(id), executorService)
22 | .exceptionally(ex -> -1)
23 | .orTimeout(1250, TimeUnit.MILLISECONDS)
24 | .exceptionally(ex -> -2);
25 | return new ProductDto(id, product.join(), rating.join());
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/aggregator/ProductDto.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08.aggregator;
2 |
3 | public record ProductDto(int id,
4 | String description,
5 | int rating) {
6 | }
7 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec08/externalservice/Client.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec08.externalservice;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.net.URI;
7 |
8 | public class Client {
9 |
10 | private static final Logger log = LoggerFactory.getLogger(Client.class);
11 | private static final String PRODUCT_REQUEST_FORMAT = "http://localhost:7070/sec01/product/%d";
12 | private static final String RATING_REQUEST_FORMAT = "http://localhost:7070/sec01/rating/%d";
13 |
14 | public static String getProduct(int id){
15 | return callExternalService(PRODUCT_REQUEST_FORMAT.formatted(id));
16 | }
17 |
18 | public static Integer getRating(int id){
19 | return Integer.parseInt(
20 | callExternalService(RATING_REQUEST_FORMAT.formatted(id))
21 | );
22 | }
23 |
24 | private static String callExternalService(String url){
25 | log.info("calling {}", url);
26 | try(var stream = URI.create(url).toURL().openStream()){ // stream should be closed
27 | return new String(stream.readAllBytes()); // responsive size is small
28 | }catch (Exception e){
29 | throw new RuntimeException(e);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec01ThreadLocal.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.UUID;
9 | /*
10 | A simple demo of thread local
11 | */
12 | public class Lec01ThreadLocal {
13 |
14 | private static final Logger log = LoggerFactory.getLogger(Lec01ThreadLocal.class);
15 | private static final ThreadLocal SESSION_TOKEN = new ThreadLocal<>();
16 |
17 | public static void main(String[] args) {
18 |
19 | Thread.ofVirtual().name("1").start( () -> processIncomingRequest());
20 | Thread.ofVirtual().name("2").start( () -> processIncomingRequest());
21 |
22 | CommonUtils.sleep(Duration.ofSeconds(1));
23 |
24 | }
25 |
26 | // ** ---- below code is just to demonstrate the workflow --- **
27 |
28 | private static void processIncomingRequest(){
29 | authenticate();
30 | controller();
31 | }
32 |
33 | private static void authenticate(){
34 | var token = UUID.randomUUID().toString();
35 | log.info("token={}", token);
36 | SESSION_TOKEN.set(token);
37 | }
38 |
39 | // @Principal
40 | private static void controller(){
41 | log.info("controller: {}", SESSION_TOKEN.get());
42 | service();
43 | }
44 |
45 | private static void service(){
46 | log.info("service: {}", SESSION_TOKEN.get());
47 | callExternalService();
48 | }
49 |
50 | // This is a client to call external service
51 | private static void callExternalService(){
52 | log.info("preparing HTTP request with token: {}", SESSION_TOKEN.get());
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec02ThreadLocal.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.UUID;
9 |
10 | /*
11 | Demo of Inheritable ThreadLocal
12 | */
13 | public class Lec02ThreadLocal {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(Lec02ThreadLocal.class);
16 | private static final ThreadLocal SESSION_TOKEN = new InheritableThreadLocal<>();
17 |
18 | public static void main(String[] args) {
19 |
20 | Thread.ofVirtual().name("virtual-1").start( () -> processIncomingRequest());
21 | Thread.ofVirtual().name("virtual-2").start( () -> processIncomingRequest());
22 |
23 | CommonUtils.sleep(Duration.ofSeconds(1));
24 |
25 | }
26 |
27 | // ** ---- below code is just to demonstrate the workflow --- **
28 |
29 | private static void processIncomingRequest(){
30 | authenticate();
31 | controller();
32 | }
33 |
34 | private static void authenticate(){
35 | var token = UUID.randomUUID().toString();
36 | log.info("token={}", token);
37 | SESSION_TOKEN.set(token);
38 | }
39 |
40 | // @Principal
41 | private static void controller(){
42 | log.info("controller: {}", SESSION_TOKEN.get());
43 | service();
44 | }
45 |
46 | private static void service(){
47 | log.info("service: {}", SESSION_TOKEN.get());
48 | var threadName = "child-of-" + Thread.currentThread().getName();
49 | Thread.ofVirtual().name(threadName).start(() -> callExternalService());
50 | }
51 |
52 | // This is a client to call external service
53 | private static void callExternalService(){
54 | log.info("preparing HTTP request with token: {}", SESSION_TOKEN.get());
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec03ScopedValues.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.ArrayList;
9 | import java.util.UUID;
10 |
11 | /*
12 | A simple demo of ScopedValues - PREVIEW feature
13 | */
14 | public class Lec03ScopedValues {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec03ScopedValues.class);
17 | private static final ScopedValue SESSION_TOKEN = ScopedValue.newInstance();
18 |
19 | public static void main(String[] args) {
20 |
21 | log.info("isBound={}", SESSION_TOKEN.isBound());
22 | log.info("value={}", SESSION_TOKEN.orElse("default value"));
23 |
24 | Thread.ofVirtual().name("1").start( () -> processIncomingRequest());
25 | Thread.ofVirtual().name("2").start( () -> processIncomingRequest());
26 |
27 | CommonUtils.sleep(Duration.ofSeconds(1));
28 |
29 | }
30 |
31 | // ** ---- below code is just to demonstrate the workflow --- **
32 |
33 | private static void processIncomingRequest(){
34 | var token = authenticate();
35 |
36 | ScopedValue.runWhere(SESSION_TOKEN, token, () -> controller());
37 |
38 | //controller();
39 | }
40 |
41 | private static String authenticate(){
42 | var token = UUID.randomUUID().toString();
43 | log.info("token={}", token);
44 | return token;
45 | }
46 |
47 | // @Principal
48 | private static void controller(){
49 | log.info("controller: {}", SESSION_TOKEN.get());
50 | service();
51 | }
52 |
53 | private static void service(){
54 | log.info("service: {}", SESSION_TOKEN.get());
55 | ScopedValue.runWhere(SESSION_TOKEN, "new-token-" + Thread.currentThread().getName(), () -> callExternalService());
56 | }
57 |
58 | // This is a client to call external service
59 | private static void callExternalService(){
60 | log.info("preparing HTTP request with token: {}", SESSION_TOKEN.get());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec04SuccessOrFailure.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.StructuredTaskScope;
9 | import java.util.concurrent.ThreadLocalRandom;
10 |
11 | /*
12 | A simple demo of Structured Concurrency
13 | */
14 | public class Lec04SuccessOrFailure {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec04SuccessOrFailure.class);
17 |
18 | public static void main(String[] args) {
19 |
20 | try(var taskScope = new StructuredTaskScope<>()){
21 | var subtask1 = taskScope.fork(Lec04SuccessOrFailure::getDeltaAirfare);
22 | var subtask2 = taskScope.fork(Lec04SuccessOrFailure::failingTask);
23 |
24 | taskScope.join();
25 |
26 | log.info("subtask1 state: {}", subtask1.state());
27 | log.info("subtask2 state: {}", subtask2.state());
28 |
29 | log.info("subtask1 result: {}", subtask1.get());
30 | log.info("subtask2 result: {}", subtask2.get());
31 |
32 | }catch (Exception e){
33 | throw new RuntimeException(e);
34 | }
35 |
36 | }
37 |
38 | private static String getDeltaAirfare(){
39 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
40 | log.info("delta: {}", random);
41 | CommonUtils.sleep("delta", Duration.ofSeconds(1));
42 | return "Delta-$" + random;
43 | }
44 |
45 | private static String getFrontierAirfare(){
46 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
47 | log.info("frontier: {}", random);
48 | CommonUtils.sleep("frontier", Duration.ofSeconds(2));
49 | return "Frontier-$" + random;
50 | }
51 |
52 | private static String failingTask(){
53 | throw new RuntimeException("oops");
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec05CancelOnFailure.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.StructuredTaskScope;
9 | import java.util.concurrent.ThreadLocalRandom;
10 |
11 | /*
12 | A simple demo of Structured Concurrency where we want to cancel all the running subtasks when one of the subtasks fails
13 | */
14 | public class Lec05CancelOnFailure {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec05CancelOnFailure.class);
17 |
18 | public static void main(String[] args) {
19 |
20 | try(var taskScope = new StructuredTaskScope.ShutdownOnFailure()){
21 | var subtask1 = taskScope.fork(Lec05CancelOnFailure::getDeltaAirfare);
22 | var subtask2 = taskScope.fork(Lec05CancelOnFailure::failingTask);
23 |
24 | taskScope.join();
25 | taskScope.throwIfFailed(ex -> new RuntimeException("something went wrong"));
26 |
27 | log.info("subtask1 state: {}", subtask1.state());
28 | log.info("subtask2 state: {}", subtask2.state());
29 |
30 | // log.info("subtask1 result: {}", subtask1.get());
31 | // log.info("subtask2 result: {}", subtask2.get());
32 |
33 | }catch (Exception e){
34 | throw new RuntimeException(e);
35 | }
36 |
37 | }
38 |
39 | private static String getDeltaAirfare(){
40 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
41 | log.info("delta: {}", random);
42 | CommonUtils.sleep("delta", Duration.ofSeconds(1));
43 | return "Delta-$" + random;
44 | }
45 |
46 | private static String getFrontierAirfare(){
47 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
48 | log.info("frontier: {}", random);
49 | CommonUtils.sleep("frontier", Duration.ofSeconds(2));
50 | return "Frontier-$" + random;
51 | }
52 |
53 | private static String failingTask(){
54 | throw new RuntimeException("oops");
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec06FirstSuccess.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.StructuredTaskScope;
9 | import java.util.concurrent.ThreadLocalRandom;
10 |
11 | /*
12 | A simple demo of Structured Concurrency where we want to cancel all the running subtasks when we get the first success response
13 | */
14 | public class Lec06FirstSuccess {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec06FirstSuccess.class);
17 |
18 | public static void main(String[] args) {
19 |
20 | try(var taskScope = new StructuredTaskScope.ShutdownOnSuccess<>()){
21 | var subtask1 = taskScope.fork(Lec06FirstSuccess::failingTask);
22 | var subtask2 = taskScope.fork(Lec06FirstSuccess::getFrontierAirfare);
23 |
24 | taskScope.join();
25 |
26 | log.info("subtask1 state: {}", subtask1.state());
27 | log.info("subtask2 state: {}", subtask2.state());
28 |
29 | log.info("subtask result: {}", taskScope.result(ex -> new RuntimeException("all failed")));
30 |
31 |
32 | }catch (Exception e){
33 | throw new RuntimeException(e);
34 | }
35 |
36 | }
37 |
38 | private static String getDeltaAirfare(){
39 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
40 | log.info("delta: {}", random);
41 | CommonUtils.sleep("delta", Duration.ofSeconds(3));
42 | return "Delta-$" + random;
43 | }
44 |
45 | private static String getFrontierAirfare(){
46 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
47 | log.info("frontier: {}", random);
48 | CommonUtils.sleep("frontier", Duration.ofSeconds(2));
49 | return "Frontier-$" + random;
50 | }
51 |
52 | private static String failingTask(){
53 | throw new RuntimeException("oops");
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/Lec07ScopedValuesWithStructuredTaskScope.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import com.vinsguru.util.CommonUtils;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.time.Duration;
8 | import java.util.concurrent.StructuredTaskScope;
9 | import java.util.concurrent.ThreadLocalRandom;
10 |
11 | /*
12 | Scoped Values inheritance using Structured Task Scope
13 | */
14 | public class Lec07ScopedValuesWithStructuredTaskScope {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(Lec07ScopedValuesWithStructuredTaskScope.class);
17 | private static final ScopedValue SESSION_TOKEN = ScopedValue.newInstance();
18 |
19 | public static void main(String[] args) {
20 |
21 | ScopedValue.runWhere(SESSION_TOKEN, "token-123", Lec07ScopedValuesWithStructuredTaskScope::task);
22 |
23 | }
24 |
25 | private static void task(){
26 | try(var taskScope = new StructuredTaskScope<>()){
27 |
28 | log.info("token: {}", SESSION_TOKEN.get());
29 |
30 | var subtask1 = taskScope.fork(Lec07ScopedValuesWithStructuredTaskScope::getDeltaAirfare);
31 | var subtask2 = taskScope.fork(Lec07ScopedValuesWithStructuredTaskScope::getFrontierAirfare);
32 |
33 | taskScope.join();
34 |
35 | log.info("subtask1 state: {}", subtask1.state());
36 | log.info("subtask2 state: {}", subtask2.state());
37 |
38 | log.info("subtask1 result: {}", subtask1.get());
39 | log.info("subtask2 result: {}", subtask2.get());
40 |
41 | }catch (Exception e){
42 | throw new RuntimeException(e);
43 | }
44 | }
45 |
46 | private static String getDeltaAirfare(){
47 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
48 | log.info("delta: {}", random);
49 | log.info("token: {}", SESSION_TOKEN.get());
50 | CommonUtils.sleep("delta", Duration.ofSeconds(1));
51 | return "Delta-$" + random;
52 | }
53 |
54 | private static String getFrontierAirfare(){
55 | var random = ThreadLocalRandom.current().nextInt(100, 1000);
56 | log.info("frontier: {}", random);
57 | log.info("token: {}", SESSION_TOKEN.get());
58 | CommonUtils.sleep("frontier", Duration.ofSeconds(2));
59 | return "Frontier-$" + random;
60 | }
61 |
62 | private static String failingTask(){
63 | throw new RuntimeException("oops");
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/sec09/MapMulti.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.sec09;
2 |
3 | import java.util.stream.IntStream;
4 |
5 | public class MapMulti {
6 |
7 | public static void main(String[] args) {
8 |
9 | // IntStream.rangeClosed(1, 5)
10 | // .mapMulti((a, b) -> {
11 | // for (int i = 0; i < a; i++) {
12 | // b.accept(a);
13 | // }
14 | // })
15 | // .forEach(System.out::println);
16 |
17 | IntStream.rangeClosed(1, 5)
18 | .flatMap(i -> IntStream.rangeClosed(1, i).map(a -> i))
19 | .forEach(System.out::println);
20 |
21 |
22 |
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/01-virtual-thread-playground/virtual-thread-playground/src/main/java/com/vinsguru/util/CommonUtils.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.util;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.time.Duration;
7 | import java.util.stream.IntStream;
8 |
9 | public class CommonUtils {
10 |
11 | private static final Logger log = LoggerFactory.getLogger(CommonUtils.class);
12 |
13 | public static void sleep(String taskName, Duration duration){
14 | try {
15 | Thread.sleep(duration);
16 | } catch (InterruptedException e) {
17 | log.info("{} is cancelled", taskName);
18 | }
19 | }
20 |
21 | public static void sleep(Duration duration){
22 | try {
23 | Thread.sleep(duration);
24 | } catch (InterruptedException e) {
25 | throw new RuntimeException(e);
26 | }
27 | }
28 |
29 | public static long timer(Runnable runnable){
30 | var start = System.currentTimeMillis();
31 | runnable.run();
32 | var end = System.currentTimeMillis();
33 | return (end - start);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/02-external-services/external-services-v1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinsguru/java-virtual-thread-course/a808dcc4936f955837fad6c3400ad757838049fc/02-external-services/external-services-v1.jar
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.5.0
9 |
10 |
11 | com.vinsguru
12 | trip-advisor
13 | 0.0.1-SNAPSHOT
14 | trip-advisor
15 | Demo project for Spring Boot
16 |
17 | 21
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-web
23 |
24 |
25 | org.projectlombok
26 | lombok
27 | true
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-test
32 | test
33 |
34 |
35 |
36 |
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-maven-plugin
41 |
42 |
43 |
44 | org.projectlombok
45 | lombok
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/TripAdvisorApplication.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration;
6 |
7 | @SpringBootApplication
8 | public class TripAdvisorApplication {
9 |
10 | public static void main(String[] args) {
11 | SpringApplication.run(TripAdvisorApplication.class, args);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/AccommodationServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.Accommodation;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.core.ParameterizedTypeReference;
6 | import org.springframework.web.client.RestClient;
7 |
8 | import java.util.List;
9 |
10 | @RequiredArgsConstructor
11 | public class AccommodationServiceClient {
12 |
13 | private final RestClient client;
14 |
15 | public List getAccommodations(String airportCode) {
16 | return this.client.get()
17 | .uri("{airportCode}", airportCode)
18 | .retrieve()
19 | .body(new ParameterizedTypeReference>() {
20 | });
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/EventServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.Event;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.core.ParameterizedTypeReference;
6 | import org.springframework.web.client.RestClient;
7 |
8 | import java.util.List;
9 |
10 | @RequiredArgsConstructor
11 | public class EventServiceClient {
12 |
13 | private final RestClient client;
14 |
15 | public List getEvents(String airportCode) {
16 | return this.client.get()
17 | .uri("{airportCode}", airportCode)
18 | .retrieve()
19 | .body(new ParameterizedTypeReference>() {
20 | });
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/FlightReservationServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.FlightReservationRequest;
4 | import com.vinsguru.tripadvisor.dto.FlightReservationResponse;
5 | import lombok.RequiredArgsConstructor;
6 | import org.springframework.web.client.RestClient;
7 |
8 | @RequiredArgsConstructor
9 | public class FlightReservationServiceClient {
10 |
11 | private final RestClient client;
12 |
13 | public FlightReservationResponse reserve(FlightReservationRequest request) {
14 | return this.client.post()
15 | .body(request)
16 | .retrieve()
17 | .body(FlightReservationResponse.class);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/FlightSearchServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.Flight;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.core.ParameterizedTypeReference;
6 | import org.springframework.web.client.RestClient;
7 |
8 | import java.util.List;
9 |
10 | @RequiredArgsConstructor
11 | public class FlightSearchServiceClient {
12 |
13 | private final RestClient client;
14 |
15 | public List getFlights(String departure, String arrival){
16 | return this.client.get()
17 | .uri("/{departure}/{arrival}", departure, arrival)
18 | .retrieve()
19 | .body(new ParameterizedTypeReference>() {
20 | });
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/LocalRecommendationServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.LocalRecommendations;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.web.client.RestClient;
6 |
7 | @RequiredArgsConstructor
8 | public class LocalRecommendationServiceClient {
9 |
10 | private final RestClient restClient;
11 |
12 | public LocalRecommendations getRecommendations(String airportCode) {
13 | return this.restClient.get()
14 | .uri("{airportCode}", airportCode)
15 | .retrieve()
16 | .body(LocalRecommendations.class);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/TransportationServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.Transportation;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.web.client.RestClient;
6 |
7 | @RequiredArgsConstructor
8 | public class TransportationServiceClient {
9 |
10 | private final RestClient restClient;
11 |
12 | public Transportation getTransportation(String airportCode) {
13 | return this.restClient.get()
14 | .uri("{airportCode}", airportCode)
15 | .retrieve()
16 | .body(Transportation.class);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/client/WeatherServiceClient.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.client;
2 |
3 | import com.vinsguru.tripadvisor.dto.Weather;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.web.client.RestClient;
6 |
7 | @RequiredArgsConstructor
8 | public class WeatherServiceClient {
9 |
10 | private final RestClient restClient;
11 |
12 | public Weather getWeather(String airportCode) {
13 | return this.restClient.get()
14 | .uri("{airportCode}", airportCode)
15 | .retrieve()
16 | .body(Weather.class);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/config/ExecutorServiceConfig.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.config;
2 |
3 | import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
4 | import org.springframework.boot.autoconfigure.thread.Threading;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 |
11 | @Configuration
12 | public class ExecutorServiceConfig {
13 |
14 | @Bean
15 | @ConditionalOnThreading(Threading.VIRTUAL)
16 | public ExecutorService virtualThreadExecutor(){
17 | return Executors.newVirtualThreadPerTaskExecutor();
18 | }
19 |
20 | @Bean
21 | @ConditionalOnThreading(Threading.PLATFORM)
22 | public ExecutorService platformThreadExecutor(){
23 | return Executors.newCachedThreadPool();
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/config/ServiceClientsConfig.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.config;
2 |
3 | import com.vinsguru.tripadvisor.client.*;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.http.client.JdkClientHttpRequestFactory;
10 | import org.springframework.web.client.RestClient;
11 |
12 | import java.net.http.HttpClient;
13 | import java.util.concurrent.Executors;
14 |
15 | @Configuration
16 | public class ServiceClientsConfig {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(ServiceClientsConfig.class);
19 |
20 | @Value("${spring.threads.virtual.enabled}")
21 | private boolean isVirtualThreadEnabled;
22 |
23 | @Bean
24 | public AccommodationServiceClient accommodationServiceClient(@Value("${accommodation.service.url}") String baseUrl){
25 | return new AccommodationServiceClient(buildRestClient(baseUrl));
26 | }
27 |
28 | @Bean
29 | public EventServiceClient eventServiceClient(@Value("${event.service.url}") String baseUrl){
30 | return new EventServiceClient(buildRestClient(baseUrl));
31 | }
32 |
33 | @Bean
34 | public WeatherServiceClient weatherServiceClient(@Value("${weather.service.url}") String baseUrl){
35 | return new WeatherServiceClient(buildRestClient(baseUrl));
36 | }
37 |
38 | @Bean
39 | public TransportationServiceClient transportationServiceClient(@Value("${transportation.service.url}") String baseUrl){
40 | return new TransportationServiceClient(buildRestClient(baseUrl));
41 | }
42 |
43 | @Bean
44 | public LocalRecommendationServiceClient recommendationServiceClient(@Value("${local-recommendation.service.url}") String baseUrl){
45 | return new LocalRecommendationServiceClient(buildRestClient(baseUrl));
46 | }
47 |
48 | @Bean
49 | public FlightSearchServiceClient flightSearchServiceClient(@Value("${flight-search.service.url}") String baseUrl){
50 | return new FlightSearchServiceClient(buildRestClient(baseUrl));
51 | }
52 |
53 | @Bean
54 | public FlightReservationServiceClient reservationServiceClient(@Value("${flight-reservation.service.url}") String baseUrl){
55 | return new FlightReservationServiceClient(buildRestClient(baseUrl));
56 | }
57 |
58 | private RestClient buildRestClient(String baseUrl){
59 | log.info("base url: {}", baseUrl);
60 | var builder = RestClient.builder().baseUrl(baseUrl);
61 | if(isVirtualThreadEnabled){
62 | builder = builder.requestFactory(new JdkClientHttpRequestFactory(
63 | HttpClient.newBuilder().executor(Executors.newVirtualThreadPerTaskExecutor()).build()
64 | ));
65 | }
66 | return builder.build();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/controller/TripController.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.controller;
2 |
3 | import com.vinsguru.tripadvisor.dto.FlightReservationResponse;
4 | import com.vinsguru.tripadvisor.dto.TripPlan;
5 | import com.vinsguru.tripadvisor.dto.TripReservationRequest;
6 | import com.vinsguru.tripadvisor.service.TripPlanService;
7 | import com.vinsguru.tripadvisor.service.TripReservationService;
8 | import lombok.RequiredArgsConstructor;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | @RestController
12 | @RequestMapping("trip")
13 | @RequiredArgsConstructor
14 | public class TripController {
15 |
16 | private final TripPlanService planService;
17 | private final TripReservationService reservationService;
18 |
19 | @GetMapping("{airportCode}")
20 | public TripPlan planTrip(@PathVariable String airportCode){
21 | return this.planService.getTripPlan(airportCode);
22 | }
23 |
24 | @PostMapping("reserve")
25 | public FlightReservationResponse reserveFlight(@RequestBody TripReservationRequest request){
26 | return this.reservationService.reserve(request);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/Accommodation.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | public record Accommodation(String name,
4 | String type,
5 | int price,
6 | double rating) {
7 | }
8 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/CarRental.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | public record CarRental(String agency,
4 | int price) {
5 | }
6 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/Event.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.time.LocalDate;
4 |
5 | public record Event(String name,
6 | String description,
7 | LocalDate date) {
8 | }
9 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/Flight.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.time.LocalDate;
4 |
5 | public record Flight(String flightNumber,
6 | String airline,
7 | int price,
8 | LocalDate date,
9 | int flightDurationInMinutes) {
10 | }
11 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/FlightReservationRequest.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.time.LocalDate;
4 |
5 | public record FlightReservationRequest(String departure,
6 | String arrival,
7 | String flightNumber,
8 | LocalDate tripDate) {
9 | }
10 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/FlightReservationResponse.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.time.LocalDate;
4 | import java.util.UUID;
5 |
6 | public record FlightReservationResponse(UUID reservationId,
7 | String departure,
8 | String arrival,
9 | String flightNumber,
10 | LocalDate tripDate,
11 | int price) {
12 | }
13 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/LocalRecommendations.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.util.List;
4 |
5 | public record LocalRecommendations(List restaurants,
6 | List sightseeing) {
7 | }
8 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/PublicTransportation.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | public record PublicTransportation(String type,
4 | double price) {
5 | }
6 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/Transportation.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.util.List;
4 |
5 | public record Transportation(List carRentals,
6 | List publicTransportations) {
7 | }
8 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/TripPlan.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.util.List;
4 |
5 | public record TripPlan(String airportCode,
6 | List accommodations,
7 | Weather weather,
8 | List events,
9 | LocalRecommendations localRecommendations,
10 | Transportation transportation) {
11 | }
12 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/TripReservationRequest.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | import java.time.LocalDate;
4 |
5 | public record TripReservationRequest(String departure,
6 | String arrival,
7 | LocalDate date) {
8 | }
9 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/dto/Weather.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.dto;
2 |
3 | public record Weather(int temperature,
4 | String conditions){
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/service/TripPlanService.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.service;
2 |
3 | import com.vinsguru.tripadvisor.client.*;
4 | import com.vinsguru.tripadvisor.dto.TripPlan;
5 | import lombok.RequiredArgsConstructor;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.stereotype.Service;
9 |
10 | import java.util.Collections;
11 | import java.util.concurrent.ExecutionException;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Future;
14 |
15 | @Service
16 | @RequiredArgsConstructor
17 | public class TripPlanService {
18 |
19 | private static final Logger log = LoggerFactory.getLogger(TripPlanService.class);
20 | private final EventServiceClient eventServiceClient;
21 | private final WeatherServiceClient weatherServiceClient;
22 | private final AccommodationServiceClient accommodationServiceClient;
23 | private final TransportationServiceClient transportationServiceClient;
24 | private final LocalRecommendationServiceClient localRecommendationServiceClient;
25 | private final ExecutorService executor;
26 |
27 | public TripPlan getTripPlan(String airportCode){
28 | var events = this.executor.submit(() -> this.eventServiceClient.getEvents(airportCode));
29 | var weather = this.executor.submit(() -> this.weatherServiceClient.getWeather(airportCode));
30 | var accommodations = this.executor.submit(() -> this.accommodationServiceClient.getAccommodations(airportCode));
31 | var transportation = this.executor.submit(() -> this.transportationServiceClient.getTransportation(airportCode));
32 | var recommendations = this.executor.submit(() -> this.localRecommendationServiceClient.getRecommendations(airportCode));
33 | return new TripPlan(
34 | airportCode,
35 | getOrElse(accommodations, Collections.emptyList()),
36 | getOrElse(weather, null),
37 | getOrElse(events, Collections.emptyList()),
38 | getOrElse(recommendations, null),
39 | getOrElse(transportation, null)
40 | );
41 | }
42 |
43 | private T getOrElse(Future future, T defaultValue){
44 | try {
45 | return future.get();
46 | } catch (Exception e) {
47 | log.error("error", e);
48 | }
49 | return defaultValue;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/java/com/vinsguru/tripadvisor/service/TripReservationService.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor.service;
2 |
3 | import com.vinsguru.tripadvisor.client.FlightReservationServiceClient;
4 | import com.vinsguru.tripadvisor.client.FlightSearchServiceClient;
5 | import com.vinsguru.tripadvisor.dto.Flight;
6 | import com.vinsguru.tripadvisor.dto.FlightReservationRequest;
7 | import com.vinsguru.tripadvisor.dto.FlightReservationResponse;
8 | import com.vinsguru.tripadvisor.dto.TripReservationRequest;
9 | import lombok.RequiredArgsConstructor;
10 | import org.springframework.stereotype.Service;
11 |
12 | import java.util.Comparator;
13 |
14 | @Service
15 | @RequiredArgsConstructor
16 | public class TripReservationService {
17 |
18 | private final FlightSearchServiceClient searchServiceClient;
19 | private final FlightReservationServiceClient reservationServiceClient;
20 |
21 | public FlightReservationResponse reserve(TripReservationRequest request){
22 | var flights = this.searchServiceClient.getFlights(request.departure(), request.arrival());
23 | var bestDeal = flights.stream().min(Comparator.comparingInt(Flight::price));
24 | var flight = bestDeal.orElseThrow(() -> new IllegalStateException("no flights found"));
25 | var reservationRequest = new FlightReservationRequest(request.departure(), request.arrival(), flight.flightNumber(), request.date());
26 | return this.reservationServiceClient.reserve(reservationRequest);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 | # upstream service properties
3 | # planning services
4 | accommodation.service.url=http://localhost:7070/sec02/accommodations/
5 | event.service.url=http://localhost:7070/sec02/events/
6 | local-recommendation.service.url=http://localhost:7070/sec02/local-recommendations/
7 | transportation.service.url=http://localhost:7070/sec02/transportation/
8 | weather.service.url=http://localhost:7070/sec02/weather/
9 |
10 | # search and reservation services
11 | flight-search.service.url=http://localhost:7070/sec03/flight/search/
12 | flight-reservation.service.url=http://localhost:7070/sec03/flight/reserve/
13 |
14 | # virtual thread enabled/disabled
15 | spring.threads.virtual.enabled=true
--------------------------------------------------------------------------------
/03-trip-advisor-application/trip-advisor/src/test/java/com/vinsguru/tripadvisor/RestClientTests.java:
--------------------------------------------------------------------------------
1 | package com.vinsguru.tripadvisor;
2 |
3 | import com.vinsguru.tripadvisor.dto.Accommodation;
4 | import com.vinsguru.tripadvisor.dto.FlightReservationRequest;
5 | import com.vinsguru.tripadvisor.dto.FlightReservationResponse;
6 | import com.vinsguru.tripadvisor.dto.Weather;
7 | import org.junit.jupiter.api.Test;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.core.ParameterizedTypeReference;
11 | import org.springframework.web.client.RestClient;
12 |
13 | import java.time.LocalDate;
14 | import java.util.List;
15 |
16 | /*
17 | This is a simple demo to play with RestClient
18 | Not spring unit tests / integrations tests
19 | */
20 | class RestClientTests {
21 |
22 | private static final Logger log = LoggerFactory.getLogger(RestClientTests.class);
23 |
24 | @Test
25 | void simpleGet() {
26 | var client = RestClient.create();
27 | var response = client.get()
28 | .uri("http://localhost:7070/sec02/weather/LAS")
29 | .retrieve()
30 | .body(Weather.class);
31 | log.info("response: {}", response);
32 | }
33 |
34 | @Test
35 | void baseUrl() {
36 | var client = RestClient.builder()
37 | .baseUrl("http://localhost:7070/sec02/weather/")
38 | .build();
39 |
40 | var response = client.get()
41 | .uri("{airportCode}", "LAS")
42 | .retrieve()
43 | .body(Weather.class);
44 | log.info("response: {}", response);
45 | }
46 |
47 | @Test
48 | void listResponse() {
49 | var client = RestClient.builder()
50 | .baseUrl("http://localhost:7070/sec02/accommodations/")
51 | .build();
52 |
53 | var response = client.get()
54 | .uri("{airportCode}", "LAS")
55 | .retrieve()
56 | .body(new ParameterizedTypeReference>() {
57 | });
58 | log.info("response: {}", response);
59 | }
60 |
61 | @Test
62 | void postRequest() {
63 | var client = RestClient.builder()
64 | .baseUrl("http://localhost:7070/sec03/flight/reserve/")
65 | .build();
66 | var request = new FlightReservationRequest("ATL", "LAS", "UA789", LocalDate.now());
67 | var response = client.post()
68 | .body(request)
69 | .retrieve()
70 | .body(FlightReservationResponse.class);
71 | log.info("response: {}", response);
72 | }
73 |
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/04-jmeter-test-scripts/trip-plan.jmx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 | continue
15 |
16 | -1
17 | false
18 |
19 | 10
20 | 10
21 | true
22 | true
23 | 60
24 |
25 | true
26 |
27 |
28 |
29 | false
30 |
31 |
32 |
33 | localhost
34 | 8080
35 | /trip/LAS
36 | GET
37 | true
38 | false
39 | true
40 | false
41 | false
42 | false
43 | false
44 | 6
45 | false
46 | 0
47 |
48 |
49 |
50 |
51 | false
52 |
53 | saveConfig
54 |
55 |
56 | true
57 | true
58 | true
59 |
60 | true
61 | true
62 | true
63 | true
64 | false
65 | true
66 | true
67 | false
68 | false
69 | false
70 | true
71 | false
72 | false
73 | false
74 | true
75 | 0
76 | true
77 | true
78 | true
79 | true
80 | true
81 | true
82 |
83 |
84 |
85 |
86 |
87 |
88 | false
89 |
90 | saveConfig
91 |
92 |
93 | true
94 | true
95 | true
96 |
97 | true
98 | true
99 | true
100 | true
101 | false
102 | true
103 | true
104 | false
105 | false
106 | false
107 | true
108 | false
109 | false
110 | false
111 | true
112 | 0
113 | true
114 | true
115 | true
116 | true
117 | true
118 | true
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/04-jmeter-test-scripts/trip-reserve.jmx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 | continue
15 |
16 | -1
17 | false
18 |
19 | 10
20 | 10
21 | true
22 | true
23 | 60
24 |
25 | true
26 |
27 |
28 |
29 | true
30 |
31 |
32 |
33 | false
34 | {
35 | "departure": "ATL",
36 | "arrival": "LAS",
37 | "date": "2024-12-12"
38 | }
39 | =
40 |
41 |
42 |
43 | localhost
44 | 8080
45 | /trip/reserve
46 | POST
47 | true
48 | false
49 | true
50 | false
51 | false
52 | false
53 | false
54 | 6
55 | false
56 | 0
57 |
58 |
59 |
60 |
61 |
62 | Content-Type
63 | application/json
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | false
72 |
73 | saveConfig
74 |
75 |
76 | true
77 | true
78 | true
79 |
80 | true
81 | true
82 | true
83 | true
84 | false
85 | true
86 | true
87 | false
88 | false
89 | false
90 | true
91 | false
92 | false
93 | false
94 | true
95 | 0
96 | true
97 | true
98 | true
99 | true
100 | true
101 | true
102 |
103 |
104 |
105 |
106 |
107 |
108 | false
109 |
110 | saveConfig
111 |
112 |
113 | true
114 | true
115 | true
116 |
117 | true
118 | true
119 | true
120 | true
121 | false
122 | true
123 | true
124 | false
125 | false
126 | false
127 | true
128 | false
129 | false
130 | false
131 | true
132 | 0
133 | true
134 | true
135 | true
136 | true
137 | true
138 | true
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java 21 - Virtual Thread MasterClass
2 |
3 | This repoistory contains the source code for my [**Java Virtual Thread**](https://www.udemy.com/course/java-virtual-thread/) course on Udemy.
4 |
5 | 
6 |
7 | Dive deep into the intricacies of concurrency & Learn how to make your Java applications run smoother and faster using simple and effective concurrency techniques. From the basics to hands-on projects, this course equips you with the skills to revolutionize your approach to programming.
8 |
9 | ## Course Highlights:
10 |
11 | - **Understanding Concurrency**: Learn about the challenges of traditional threads and discover how Java Virtual Threads provide a simpler and more efficient solution for making your programs scale seamlessly.
12 |
13 | - **Executor Service Mastery**: Dive into using Executor Service with Virtual Threads. Overcome challenges, control concurrency, and process tasks more efficiently by breaking them down into parallel subtasks.
14 |
15 | - **Completable Future Exploration**: Explore Completable Future for asynchronous task processing with Virtual Threads. Learn practical methods, handle timeouts, and manage errors in a declarative style.
16 |
17 | - **Preview of Structured Concurrency**: Get a sneak peek into Java's new preview APIs, giving you insights into the future of concurrent programming. Stay ahead of the curve with a simplified understanding of Java's concurrency development.
18 |
19 | - **Practical Application Development**: Apply what you've learned in a hands-on project using Spring Boot Web and Virtual Threads. See firsthand how these concepts integrate seamlessly into real-world application development.
20 |
21 | - **Scalability Testing with JMeter**: Test your application's scalability using JMeter. Compare throughput and response time to ensure optimal efficiency under different conditions.
22 |
23 | - **Migration Made Easy**: Conclude the course with a practical migration guide to transition your existing applications to Java Virtual Threads effortlessly. Get ready for a future where your programs effortlessly combine concurrency and scalability.
24 |
25 | Unlock the full potential of Java Virtual Threads and elevate your programming skills. Enroll now to reshape the way you approach scalability and performance in Java applications!
--------------------------------------------------------------------------------