├── .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 | ![](.doc/java-virtual-thread.png) 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! --------------------------------------------------------------------------------