{"id":18291056,"url":"https://github.com/hakdogan/loom-examples","last_synced_at":"2025-08-24T19:16:19.443Z","repository":{"id":184906633,"uuid":"672633482","full_name":"hakdogan/loom-examples","owner":"hakdogan","description":"This repository contains examples of Project Loom parts such as Virtual Thread, Structured Concurrency, and Scoped Values","archived":false,"fork":false,"pushed_at":"2024-08-02T18:30:25.000Z","size":1179,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-04T13:53:19.099Z","etag":null,"topics":["concurrency","continuations","threads","virtual-threads"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hakdogan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-30T18:34:00.000Z","updated_at":"2024-08-02T18:30:28.000Z","dependencies_parsed_at":"2023-12-20T19:26:31.391Z","dependency_job_id":"65857cbd-4a0c-4f70-bd43-cdcf3dedf44b","html_url":"https://github.com/hakdogan/loom-examples","commit_stats":null,"previous_names":["hakdogan/loom-examples"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hakdogan%2Floom-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hakdogan%2Floom-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hakdogan%2Floom-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hakdogan%2Floom-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hakdogan","download_url":"https://codeload.github.com/hakdogan/loom-examples/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247324486,"owners_count":20920655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concurrency","continuations","threads","virtual-threads"],"created_at":"2024-11-05T14:13:04.328Z","updated_at":"2025-04-05T10:30:55.329Z","avatar_url":"https://github.com/hakdogan.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Loom Examples\n\nThis repository contains examples of `Project Loom` parts such as `Virtual Threads`, `Structured Concurrency`, and `Scoped Values`\n\n# Requirements\n\n* JDK 22 or later\n\n# Virtual Threads\n\nVirtual threads are lightweight implementations of `java.lang.Thread` and they promise to write highly scalable concurrent applications. The main benefit of Virtual Threads is that you can stick to the familiar `thread-per-request programming model` without scaling problems.\n\n[virtual-threads](https://github.com/hakdogan/loom-examples/tree/main/virtual-threads) module elucidates the basic behaviors and building blocks of Virtual Threads with some examples.\n\n## Starting Virtual Thread\n\nJDK provides factory methods on the new `builder interface` to create Virtual Threads.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.factory.PlatformThreadPerTask.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/factory/PlatformThreadPerTask.java) shows the natural boundaries of creating platform threads. The boundaries are related whit system resources and remember that, it can be different based on your system resource.\n\n```java\ntry(var executor = Executors.newCachedThreadPool()){\n            IntStream.range(0, 5000)\n                    .forEach(i -\u003e {\n                        executor.submit(() -\u003e {\n                            Thread.sleep(Duration.ofSeconds(1));\n                            return i;\n                        });\n                    });\n        }\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.factory.VirtualThreadPerTask.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/factory/VirtualThreadPerTask.java) shows how to use the new `newVirtualThreadPerTaskExecutor` to start a Virtual Thread for each task.\n\n```java\ntry(var executor = Executors.newVirtualThreadPerTaskExecutor()){\n        IntStream.range(0, 100_000).forEach(i -\u003e {\n                executor.submit(() -\u003e {\n                Thread.sleep(Duration.ofSeconds(1));\n                return i;\n            });\n        });\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.factory.StartVirtualThread.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/factory/StartVirtualThread.java) shows how to use the new `startVirtualThread` factory method to start a Virtual Thread.\n\n```java\nThread.startVirtualThread(() -\u003e System.out.println(\"Hello from Virtual Thread\"));\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.builder.Unstarted.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/builder/Unstarted.java) shows how to create a Virtual Thread that will not be started until the `start()` method is invoked with the new `Builder API`.\n\n```java\nThread.ofVirtual().unstarted(() -\u003e System.out.println(\"Hello from postponed Virtual Thread\"));\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.builder.Factory.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/builder/Factory.java)  shows how to use `ThreadFactory` to create Virtual Threads.\n\n```java\n    var virtualThreadFactory = Thread.ofVirtual().factory();\n    runWithExecutor(platformThreadFactory);\n\n    var virtualThread = virtualThreadFactory.newThread(Factory::sayHello);\n    virtualThread.start();\n    \n    ...\n\n    private static void runWithExecutor(final ThreadFactory threadFactory){\n\n        try (var executor = Executors.newThreadPerTaskExecutor(threadFactory)) {\n            IntStream.rangeClosed(0, 4).forEach(i -\u003e\n                executor.submit(() -\u003e {\n                    Thread.sleep(Duration.ofSeconds(1));\n                    System.out.println(\"Is virtual: \" + Thread.currentThread().isVirtual());\n                    return i;\n                }));\n            }\n        }\n```\n\u003c/details\u003e\n\n## Continuations\n\nIn `Project Loom`, a `continuation` is an object that may suspend or yield execution at some point by itself and, when resumed or invoked, carries out the rest of some computation.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.continuation.YieldExecution.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/continuation/YieldExecution.java) shows the yield execution behavior of the `Continuation` object.\n\n```java\n        ContinuationScope scope = new ContinuationScope(SCOPE_NAME);\n\n        Continuation continuation = new Continuation(scope, () -\u003e {\n            System.out.println(\"Continuation is running\");\n            Continuation.yield(scope);\n            System.out.println(\"Continuation is still running\");\n        });\n\n        continuation.run();\n```\n\n```shell\njava --add-exports java.base/jdk.internal.vm=ALL-UNNAMED \\\nsrc/main/java/org/jugistanbul/virtualthread/continuation/YieldExecution.java\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.jump.ThreadJump.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/jump/ThreadJump.java) shows the `thread jump` behavior that may be observed when a Virtual Thread `mounts` on a Platform Thread again after `unmounted`.\n\n```java\n    var threadList = IntStream.range(0, 10)\n        .mapToObj(i -\u003e Thread.ofVirtual().unstarted(() -\u003e {\n\n            if(i == 0) {\n                System.out.println(Thread.currentThread());\n            }\n    \n            ThreadUtil.sleepOfMillis(25);\n    \n            if(i == 0) {\n                System.out.println(Thread.currentThread());\n            }\n\n    })).toList();\n\n    threadList.forEach(Thread::start);\n    ThreadUtil.joinAll(threadList);\n```\n\u003c/details\u003e\n\n## Thread Pinning\n\nThere are two cases where a blocking operation doesn't `unmount` the virtual thread from the `carrier thread`:\n\n1) When the virtual thread executes a `synchronized` block or method code\n2) When it calls a `native method` or a `foreign function`\n\nIn these cases, the virtual thread is pinned to the carrier thread.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.pin.ThreadPinned.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/pin/ThreadPinned.java) shows the `pinning event` that occurs when the virtual thread executes a `synchronized block` or method code.\n\n```java\n        var threadList = IntStream.range(0, 10)\n                .mapToObj(i -\u003e Thread.ofVirtual().unstarted(() -\u003e {\n\n                    if (i == 0) {\n                        System.out.println(Thread.currentThread());\n                    }\n\n                    synchronized (lock) {\n                        ThreadUtil.sleepOfMillis(25);\n                    }\n\n                    if (i == 0) {\n                        System.out.println(Thread.currentThread());\n                    }\n\n                })).toList();\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.pin.PreventPinning.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/pin/PreventPinning.java) shows how to prevent `pinning event`.\n\n```java\n    var threadList = IntStream.range(0, 10)\n        .mapToObj(i -\u003e Thread.ofVirtual().unstarted(() -\u003e {\n\n        if (i == 0) {\n            System.out.println(Thread.currentThread());\n        }\n\n        lock.lock();\n        try {\n            ThreadUtil.sleepOfMillis(25);\n        } finally {\n            lock.unlock();\n        }\n\n        if (i == 0) {\n            System.out.println(Thread.currentThread());\n        }\n    })).toList();\n```\n\u003c/details\u003e\n\n## Monitoring\n\nIn addition to existing ones, there are several new runtime parameters and events that the JDK provides to be able to monitor behaviors related to virtual threads.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.pool.ListPlatformThreads.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/pool/ListPlatformThreads.java) shows a way to observe how many `platform threads` are used to run N number of virtual threads.\n\n\n```java\n    var threadList = IntStream\n        .range(0, 100_000)\n        .mapToObj(_ -\u003e Thread.ofVirtual().unstarted(() -\u003e {\n\n            var poolName = getPoolName();\n            poolNames.add(poolName);\n    \n            var workerName = getWorkerName();\n            pThreadNames.add(workerName);\n\n        })).toList();\n\n        var start = Instant.now();\n        threadList.forEach(Thread::start);\n        ThreadUtil.joinAll(threadList);\n\n        System.out.println(STR.\"Execution time:  \\{ThreadUtil.benchmark(start)} ms\");\n        System.out.println(STR.\"Core             \\{Runtime.getRuntime().availableProcessors()}\");\n        System.out.println(STR.\"Pools            \\{poolNames.size()}\");\n        System.out.println(STR.\"Platform threads \\{pThreadNames.size()}\");\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.monitor.MonitoringPinningEvent.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/monitor/MonitoringPinningEvent.java)  shows how to monitor the `pinning event` using `jdk.tracePinnedThreads` flag.\n\n```shell\njava --enable-preview --source 22 \\\n-cp ../util/target/classes/ \\\n-Djdk.tracePinnedThreads=short \\\nsrc/main/java/org/jugistanbul/virtualthread/monitor/MonitoringPinningEvent.java\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.monitor.NativeMemoryTracking.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/monitor/NativeMemoryTracking.java) shows how the amount of memory allocated to threads(Platform and Virtual) can be observed with jcmd and JFR through NMT.\n\n```java\n        var threadCount = defineThreadCount(args[0]);\n        var threadType  = defineThreadType(args[1]);\n        var jcmd        = args.length \u003e= 3 \u0026\u0026 defineUsedJcmd(args[2]);\n        var printTime   = threadCount - 1;\n\n        System.out.println(STR.\"Thread count set to \\{threadCount}\");\n\n        try(var executor = defineExecutorService(threadType)){\n\n            IntStream.range(0, threadCount).forEach(i -\u003e {\n\n                if(jcmd \u0026\u0026 i == printTime){\n                    memoryTracking(pid, threadType);\n                }\n\n                executor.execute(() -\u003e ThreadUtil.sleepOfSeconds(5));\n            });\n        }\n```\n```shell\nsh runNativeMemoryTracking.sh 12000 VIRTUAL false #don't use jcmd to access nmt\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.scheduler.CooperativeScheduling.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/scheduler/CooperativeScheduling.java) shows the way to observe scheduler behavior with runtime parameters such as\n\n- jdk.virtualThreadScheduler.parallelism\n- jdk.virtualThreadScheduler.maxPoolSize\n- jdk.virtualThreadScheduler.minRunnable\n\n```shell\njava --enable-preview --source 22 \\\n-cp ../util/target/classes/ \\\n-Djdk.virtualThreadScheduler.parallelism=1 \\\n-Djdk.virtualThreadScheduler.maxPoolSize=1 \\\n-Djdk.virtualThreadScheduler.minRunnable=1 \\\nsrc/main/java/org/jugistanbul/virtualthread/scheduler/CooperativeScheduling.java\n```\n\u003c/details\u003e\n\n## CPU Bound Workloads\n\nVirtual Threads offer a scalability benefit for `IO-bound` workloads, but relatively little for `CPU-bound` ones.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.virtualthread.boundary.CpuBounded.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/virtual-threads/src/main/java/org/jugistanbul/virtualthread/boundary/CpuBounded.java) shows a way to observe the difference in scheduler behavior when virtual and platform threads are used in `CPU-bound` workloads.\n\n\n```java\n    IntStream\n        .rangeClosed(1, 64)\n        .forEach(index -\u003e {\n\n            Instant start = Instant.now();\n            executor.submit(() -\u003e {\n                IntStream\n                    .range(0, 50_000_000)\n                    .mapToObj(BigInteger::valueOf)\n                    .reduce(BigInteger.ZERO, BigInteger::add);\n\n                System.out.println(STR.\"\\{createTwoDigitId(index)};\\{ThreadUtil.benchmark(start)}\");\n            });\n        });\n\n        ThreadUtil.shutdownAndAwaitTermination(executor, TimeUnit.HOURS);\n```\n\n![](images/newCachedThreadPool.png)\n![](images/newVirtualThreadPerTaskExecutor.png)\n\u003c/details\u003e\n\n# Structured Concurrency\n\nVirtual Threads solve the cost and efficiency issues of threads, but managing the resulting large number of threads is still a challenge. Structured concurrency overcomes this problem by treating groups of related tasks running on different threads as a single unit of work.\n\n[structured-concurrency](https://github.com/hakdogan/loom-examples/tree/main/structured-concurrency) module elucidates the fundamental principles and components of Structured Concurrency through illustrative examples.\n\nThe examples in this module show the short-circuiting behavior that structured concurrency provides with cancellation propagation when any of the subtasks fails or succeeds.  This is useful to prevent unnecessary work.\n\n## Shutdown on Failure\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.concurrency.structured.exchange.ShutDownOnFailure.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/structured-concurrency/src/main/java/org/jugistanbul/concurrency/structured/exchange/ShutDownOnFailure.java) shows the short-circuiting behavior that structured concurrency provides with cancellation propagation when any of the subtasks fails.\n\n```java\n        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n\n            Subtask\u003cBigDecimal\u003e usd = scope.fork(ExchangeReader::fetchUsdExchangeRate);\n            Subtask\u003cBigDecimal\u003e euro = scope.fork(ExchangeReader::fetchEuroExchangeRate);\n\n            scope.join().throwIfFailed();\n            System.out.printf(\"Euro USD parity is %.2f\", euro.get().divide(usd.get(), RoundingMode.HALF_EVEN));\n        }\n```        \n\u003c/details\u003e\n\n## Shutdown on Success\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.concurrency.structured.exchange.ShutDownOnSuccess.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/structured-concurrency/src/main/java/org/jugistanbul/concurrency/structured/exchange/ShutDownOnSuccess.java) shows the short-circuiting behavior that structured concurrency provides with cancellation propagation when any of the subtasks succeed which is useful to prevent unnecessary work once a successful result is obtained.\n\n```java\n        try (var scope = new StructuredTaskScope.ShutdownOnSuccess\u003c\u003e()) {\n\n            StructuredTaskScope.Subtask\u003cBigDecimal\u003e usd = scope.fork(ExchangeReader::fetchUsdExchangeRate);\n            StructuredTaskScope.Subtask\u003cBigDecimal\u003e euro = scope.fork(ExchangeReader::fetchEuroExchangeRate);\n\n            scope.join();\n\n            System.out.println(STR.\"USD process state  : \\{usd.state()}\");\n            System.out.println(STR.\"EURO process state :  \\{euro.state()}\");\n\n            System.out.println(scope.result());\n        }\n```\n\n```shell\nThe remote service call will be performed to fetch the USD exchange rate.\nThe remote service call will be performed to fetch the Euro exchange rate.\nUSD process state  : UNAVAILABLE\nEURO process state :  SUCCESS\n28.94\n```\n\u003c/details\u003e\n\n# Scoped Values\n\nThe Scoped Values API allows us to store and share immutable data for a bounded lifetime. The example in this module shows this facility with a concrete example.\n\n\u003cdetails\u003e\n\u003csummary\u003eorg.jugistanbul.handler.RequestHandler.java\u003c/summary\u003e\n\n[This example](https://github.com/hakdogan/loom-examples/blob/main/scoped-values/src/main/java/org/jugistanbul/handler/RequestHandler.java) shows how to share immutable data safely and efficiently for a bounded lifetime by using one-way data transfer between components.\n\n```java\n//RequestHandler.java\nScopedValue.where(PRINCIPAL, authority).run(() -\u003e {\n    var access = Database.access();\n    ...\n});\n\n//Database.java\npublic static boolean access(){\n    var authority = PRINCIPAL.get();\n    return \"admin\".equals(authority.username());\n}\n```\n\n```shell\nhttp :8080 username==admin password==12345\n\nHTTP/1.1 200 OK\nContent-Type: text/plain\n\nPermission: true\n```\n\u003c/details\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhakdogan%2Floom-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhakdogan%2Floom-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhakdogan%2Floom-examples/lists"}