17 ms·
JEP proposed to target JDK 19: 425: Virtual Threads (Preview)
- dang 4y agoRecent and related: Achieving 5M persistent connections with Project Loom virtual threads - https://news.ycombinator.com/item?id=31214253 https://news.ycombinator.com/item?id=31214253 - April 2022 (142 comments)
- sydthrowaway 4y agoIsnt this the Google Balloon
- sidpatil 4y agoNo, that's Loon. https://x.company/projects/loon/ https://x.company/projects/loon/
- binarynate 4y agoWith Loom, what's the equivalent of the following Promise concepts? 1. Promise constructor (to turn an existing callback-based or async operation into one that can be blocked with Loom). JS example: await new Promise(resolve => someApiThatTakesACallback(resolve)); 2. Methods like Promise.all() and Promise.any() to run multiple async operations concurrently. JS example: const [result1, result2] = await Promise.all([doSomething1(), doSomething2()]);
- christophilus 4y agoMy guess for the latter is something like goroutines and channels, though I’m not sure. So: spin up a thread per “doSomething” and then join those in your main thread.
- teh64 4y agoFor the first one, I believe this is the correct method [2], which takes a Callable (the other submit methods take a Runnable, which does not seem to be generic). For the second one, you can use an implementation of ExecutorService[1], which has the methods invokeAll and invokeAny. Futures in Java are similar to Promises in Javascript, in that they represent a calculation that might not be completed yet. 1. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html https://docs.oracle.com/en/java/javase/17/docs/api/java.base... 2. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html#submit(java.util.concurrent.Callable) https://docs.oracle.com/en/java/javase/17/docs/api/java.base...
- ackfoobar 4y agoThe parent comment meant `callbackTakingFunction: ((Res, Throwable) -> ()) -> ()`, not `blockingFunction: () -> Res` `ExecutorService#submit` works with the latter.
- binarynate 4y agoThank you!
- ackfoobar 4y ago1. Callback interop I was expecting something like call/cc, or `suspend(Cancellable)Coroutine` in Kotlin. But judging from the list of preview items[1] it seems the only way is to have the callback write the result to a Future and blocking wait on it. 2.1 Promise.all() can be done with `.map(future => future.get())` But it is a bit more complicated than that. [2] 2.2 Promise.any() This is somewhat relevant http://mail.openjdk.java.net/pipermail/loom-dev/2020-February/001019.html http://mail.openjdk.java.net/pipermail/loom-dev/2020-Februar... [1] https://download.java.net/java/early_access/loom/docs/api/preview-list.html https://download.java.net/java/early_access/loom/docs/api/pr... [2] https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutin... "This function is not equivalent to deferreds.map { it.await() } which fails only when it sequentially gets to wait for the failing deferred, while this awaitAll fails immediately as soon as any of the deferreds fail."
- binarynate 4y agoThanks!
- twic 4y agoHere are my guesses. 1. Same as with normal threads: CompletableFuture<Foo> future = new CompletableFuture<>(); someApiThatTakesACallback(future::complete); Foo result = future.get(); // this can take a timeout 2. If you mean that the operations are blocking and should be called in parallel using virtual threads, the equivalent of Promise.any:: ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); Future<Foo> future1 = executor.submit(this::doSomething1); Future<Bar> future2 = executor.submit(this::doSomething2); Foo result1 = future1.get(); Bar result2 = future2.get(); There is a version of that using ExecutorService::invokeAll, but i'm not sure it's any more concise, because you still need to unpack the futures one by one. For Promise.any: ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); Foo result = executor.invokeAny(List.of(this::doSomething1, this::doSomething2)); The asymmetry between invokeAny and invokeAll here is slightly grating, but makes sense. Note that in both cases, you probably already have an executor lying around that you can use for this.
- binarynate 4y agoThanks! Your examples are super helpful.
- samus 4y agoDo note that the `Executor` interface now extends `AutoCloseable` and can now be used with the `try-with-resources`-statement. This is the most important building block for the new Structured Concurrency approach!
- papercrane 4y agoIn addition to the other comments, the other way to achieve this with loom is to use the new structured concurrency API. This is overkill for a single task, but for your second example you could do a an invokeAny with something like this: try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) { scope.fork(() -> doSomething1()); scope.fork(() -> doSomething2()); scope.join(); // or if you wanted a 5 second timeout .joinUntil(Instant.now().plusSeconds(5)) String result = scope.result(); // This will return the result of either doSomething1() or doSomething2(), whichever won the race to finish first. ... } https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html https://download.java.net/java/early_access/loom/docs/api/jd...
- pgh 4y agoThis is very reminiscent of the m:n thread support of Java on Solaris. https://docs.oracle.com/cd/E19620-01/805-4031/6j3qv1oej/index.html https://docs.oracle.com/cd/E19620-01/805-4031/6j3qv1oej/inde...
- SemanticStrengh 4y agoWhat is old is new
- abledon 4y agoIf only Java had a slick CLI tool like "dotnet" for C#. man that thing can do so much, so easily
- SemanticStrengh 4y agoYou mean JShell? (yes the naming is perfect)
- twic 4y agoNo, the dotnet tool is more like Gradle or Cargo. You say "dotnet new" to set up a new project, "dotnet build" to build it, "dotnet nuget push" to release to an artifact repository, etc. The JDK doesn't have this because many of the concerns involved are outside its remit. Java does not define a project layout, a build process, or a way to acquire or distribute libraries. But the Java world does have Gradle and Maven, which are, for better or for worse, the options to satisfy abledon's desire.
- SemanticStrengh 4y agoThere is also Jlink and yeah I get your point, Gradle has moderate complexity and is extremely featureful.
- abledon 4y agothat seems to be a REPL. dotnet cli is more broad, great developer ergonomics imo. terse/concise design https://docs.microsoft.com/en-us/dotnet/core/tools/ https://docs.microsoft.com/en-us/dotnet/core/tools/ e.g. to make a new console app.... ``` c# dotnet new console java go install extra maven program (if your team uses maven)... mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 ```
- twic 4y agoOne question: virtual threads are lightweight, but are the executors which create them? That is, should i organise my code so i create a single executor and use it across many operations, or is it okay to create and destroy executors all over the place? And are the executors threadsafe - both for use from virtual threads, and from native ones? As an aside, these are things which is important to know, but which are very rarely documented. We had some headaches a while ago because we wrote code which treated the new HttpClient as lightweight when it is very much not.
- kjeetgill 4y agoThe heaviest part of executors are the task queues and the threads themselves. If you're using 2 or 3 executors over a single one, you're presumably using the same amount of space in the queue and number of threads, so the "weight" doesn't change except that the queues are concurrent data structures so can be sources of contention ... in really odd hyperactive scenarios. Executors tend to be units of management (await()/shutdown()) etc. and monitoring, so that'll be your main reason to use more than just one for everything.
- reggieband 4y ago> When code running in a virtual thread calls a blocking I/O operation in the java.* API, the runtime performs a non-blocking OS call and automatically suspends the virtual thread until it can be resumed later. How does it know that an operation is blocking on I/O? Is this limited to stuff baked into the language or standard library? What is the mechanism of suspension and resumption? They mention elsewhere that any platform thread could pick up any virtual thread so I assume they must be storing the stack somewhere. Is there a cost transferring stacks on virtual threads across platform threads? Does this introduce new security implications if there are less OS level restrictions on memory access between platform threads? What happens in cases where an application has user defined platform threads? How does the system determine what platform threads are available to the virtual threading system? I think this is probably the right decision for Java and I agree with all of their motivations. I personally like the explicitness of async/await, however I assume Java devs are very familiar with threading in general. I believe this allows an easier path to migrating existing Java code.
- kevincox 4y ago> Is there a cost transferring stacks on virtual threads across platform threads? You shouldn't need to "transfer" the stack. There isn't really a "platform stack", that is just whatever your stack pointer is pointing at. So it is perfectly fine to allocate many stacks and switch between them within one OS thread just by changing the stack pointer. Of course if you are allocating a "full stack" then the main question is what is the point? IIUC the biggest cost of threads is the memory allocated to the stack. So unless you are doing something clever you don't get much benefit. There are many approaches here but I guess it is up to the runtime to pick one.
- aardvark179 4y agoThe largest cost is that you are allocating a fixed size stack, and that usually has to be big enough for whatever thread you might run. Virtual threads only have a stack as big as they require, and that’s likely to be fairly small. A lot of thought went into the foot print of virtual threads, we had long discussions about individual fields!
- Traubenfuchs 4y agoCan you interpret this as a global infinitely scaling thread pool? Or as syntactic sugar for interacting with such a threadpool? I guess I still haven‘t understood what this gives you as opposed to submitting lambdas to a threadpool. Besides maybe more usefull threadlocals.
- moonchild 4y agoYou don't need to CPS by hand.
- deleted 4y ago[deleted]
- theptip 4y agoI found the JEP to be very well-written. A good summarizing paragraph: > Virtual threads are a lightweight implementation of threads that is provided by the JDK rather than the OS. They are a form of user-mode threads, which have been successful in other multithreaded languages (e.g., goroutines in Go and processes in Erlang). User-mode threads even featured as so-called "green threads" in early versions of Java, when OS threads were not yet mature and widespread. However, Java's green threads all shared one OS thread (M:1 scheduling) and were eventually outperformed by platform threads, implemented as wrappers for OS threads (1:1 scheduling). Virtual threads employ M:N scheduling, where a large number (M) of virtual threads is scheduled to run on a smaller number (N) of OS threads
- mike_hearn 4y agoWhat this means is: Loom ships in preview form in September, and if all goes well will probably graduate to a fully supported feature a year later. So it'll be September 2023 at the earliest before this can used. During that period you have to pass extra command line arguments to opt-in. The good news is that long term supported releases got more frequent in recent times and the next one is due in ... September 2023. So there hopefully won't be any delays for people who have to wait for an LTS release.
- pron 4y agoPreview features [1] are fully supported — and are part of the official Java SE specification — they're just not finalised or "permanent" (and are, therefore, disabled by default). I.e. the API might experience some changes due to feedback, so those who wish to use it acknowledge, by means of the --enable-preview flag, that they accept the possibility of the API changing. However, an API is marked as Preview (as opposed to incubator[2]) only when it is close to being finalised, meaning that we believe it can be finalised in two releases (one year). [1]: https://openjdk.java.net/jeps/12 https://openjdk.java.net/jeps/12 [2]: https://openjdk.java.net/jeps/11 https://openjdk.java.net/jeps/11
- RcouF1uZ4gsC 4y agoAlthough I am not a Java user, this is great news. Asynchronous code, even with a await, adds a lot of complexity. It leads to the “color” problem of functions, as well as increased complexity in languages without garbage collection (my understanding is that async is what drove a lot of the requirement for Pin in Rust. There are also advantages in debug ability and performance tracing for threads. It would be nice to have (virtual) threads that are lightweight and efficient enough that we did not need async for writing highly concurrent software. I wonder if 20 years from now, programmers will look at async, kind of like current programmers look at segmented memory or manual register allocation —- something that was necessary for performance in a bygone era, but is now not needed.
- pjmlp 4y agoLets not forget that Modula-2 already had co-routines, Ada tasks, Active Oberon active objects, Concurrent Pascal, and plenty of other examples. If anything, this is yet another example of the decades how old concepts still take to become widespread across all major mainstream languages.
- bestinterest 4y agoJava will join the (imo) better concurrency path of higher level languages with green like threads, Golang (goroutine), Erlang, Java (Loom, virtual threads) & Ruby (soon under Ractors?). I really think C# & Python will be jealous of the languages mentioned and puts them in an odd spot from language design perspective.
- aardvark179 4y agoFor Ruby I’d say the closest equivalent would be Fibers with the scheduler support introduced at 3.0. They take a different approach with pros and cons compared to virtual threads, but they are similar.
- SemanticStrengh 4y agoKotlin state of the art coroutines should be mentioned too
- deepsun 4y agoIsn't Kotlin coroutines have the same coloring problem (functions must be marked with "suspend").l? Although the coloring problem is only a problem if red functions are harder to use. Not sure marking all functions with "suspend" would make it any worse, besides infecting the codebase.
- SemanticStrengh 4y agoThe main coloring problem beyond async is the wrapping of return types in futures, which Kotlin groundbreakingly make transparent.
- ackfoobar 4y agoHere the Kotlin lead argued that it is not a "problem". https://elizarov.medium.com/how-do-you-color-your-functions-a6bb423d936d https://elizarov.medium.com/how-do-you-color-your-functions-...
- 4y ago
- nimish 4y agoCongrats! Loom is excellent.
- nu11ptr 4y agoFrom http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.h...: "Whereas the OS can support up to a few thousand active threads, the Java runtime can support millions of virtual threads. Every unit of concurrency in the application domain can be represented by its own thread, making programming concurrent applications easier. Forget about thread-pools, just spawn a new thread, one per task. You’ve already spawned a new virtual thread to handle an incoming HTTP request, but now, in the course of handling the request, you want to simultaneously query a database and issue outgoing requests to three other services? No problem — spawn more threads. You need to wait for something to happen without wasting precious resources? Forget about callbacks or reactive stream chaining — just block. Write straightforward, boring code. All the benefits threads give us — control flow, exception context, debugging flow, profiling organization — are preserved by virtual threads; only the runtime cost in footprint and performance is gone. There is no loss in flexibility compared to asynchronous programming because, as we’ll see, we have not ceded fine-grained control over scheduling." This seems so obvious in hindsight as the "right way". I wonder why we went down that whole async/await craze with so many languages?
- pkulak 4y agoWell, this kinda thing isn't free. You need a rather large and complicated runtime to make it work. Fine for the JVM, which is already a large and complicated runtime, but it turns out that making a runtime like this optional is not really possible. So languages that don't have a runtime are stuck with async/await, which can be zero-cost. And, to be fair, languages like Erlang and Go went this route LONG ago.
- SemanticStrengh 4y agoExcept GraalVM bring support to almost any language
- thfuran 4y agoThat's some hefty sleight of hand.
- homarp 4y agosee https://news.ycombinator.com/item?id=31214253 https://news.ycombinator.com/item?id=31214253 "Achieving 5M persistent connections with Project Loom virtual threads"
- djanogo 4y agoThe world biggest deployment of Java API is android, I wish Google had just bought out Java instead of copying it's API's, fighting it in court, and now using kotlin (with it's _IMO_ ugly syntactic sugar) to avoid legal issues.
- uluyol 4y agoWhy would Oracle sell Java to Google? I'm pretty sure that a main attraction for them in buying it was the possibility of collecting $$$ from Google.
- vips7L 4y agoI believe OP means that Google had the opportunity to buy Sun but didn't.
- SemanticStrengh 4y agoIt's only a matter of time before the amount of bullshit at Google reach a too strong threshold and are obligated to switch to full openjdk vs their abandonware. Unfortunately they are so mediocre they have made an incompatible bytecode so they will need some patches/translation layers atop jdk. BTW they are claiming JDK 11 support for Android 13. Of course this is a lie that only apply to the stdlib and not runtime support such as e.g constant dynamics.
- pron 4y ago> The world biggest deployment of Java API is android Not even close. The entire mobile space — Android and iOS combined — is drastically smaller than just Java alone (not counting Android). You can confirm this by going to job websites that allow you to analyse their postings. Java, together with its sister leading languages, JavaScript and Python, is so popular that it's in a completely different ballgame than most languages.
- ferdowsi 4y agoCongratulations to everyone who thought this through and implemented it. I'll go so far as to say that Project Loom makes Java a more desirable web programming language to me than any language that has colored asynchronousity, like Rust, C#, Python, etc. Async/await is one of the worst design patterns to emerge in the last few years and I'm glad that the tech world is coming to its senses.
- olliej 4y agoAre these like “fibers” or whatever that weird not-a-full thread thing on windows? I genuinely don’t know as I have never actually seen them being used, but they always seem to be described as a “lightweight” thread
- exabrial 4y agocorrect. These are "threads" that are managed by the JVM, and "context switches" do not involve a syscall.
- exabrial 4y agoI think Java has been working on getting the API "correct" for a number of years. I appreciate the pace at which they are moving! Once this is released, we're going to have it for a very long time
- jpgvm 4y agoRate of new features out of the Java team has been incredibly impressive.
- alberth 4y agoErlang. Curious to see how Loom concurrency compares to FP languages like Erlang.