3 ms·
* Java offers both user-mode and kernel threads. You pick at creation, and can even plug your own scheduler. * Loom's virtual threads are completely scheduled
by pron 6y ago
* Java offers both user-mode and kernel threads. You pick at creation, and can even plug your own scheduler.
* Loom's virtual threads are completely scheduled in library code, written in Java.
* FFI that bypasses the JDK and interacts with native code that does either IO or OS-thread synchronization is extremely rare in Java.
* Cancellation is the same for both.
Also, IMO, coordination is simpler for threads than for async. Where they differ is in their choice of defaults: thread allow scheduling points anywhere except where explicitly excluded; async/await allows scheduling points nowhere except where explicitly allowed. Putting aside that some languages have both, resulting in few if any guarantees, threads' defaults are better for correct concurrency. The reason is that correctness relies on atomicity, or lack of scheduling points in critical sections. When you explicitly exclude them, none of your callees can break your correctness. When you explicitly allow them, any callee can become async and break its caller's logic. True, the type system will show you where the relevant callsites are, but it will not show you whether there is a reliance on atomicity or not.
Async/await does, however, make sense for JavaScript, where all existing code already has an implicit assumption of atomicity, so breaking it would have broken the world. For languages that have both, async/await mostly adds a lot of complexity, although sometimes it is needed for implementation reasons.
- ryanworl 6y agoIs it possible to override the scheduler from user code? i.e. if you wanted to control scheduling yourself for some reason.
- pron 6y agoYes, and even on a per-thread basis.
- ryanworl 6y agoThanks! Is there an example "Hello World" executor that demonstrates the minimum functionality needed? I would like an executor that runs all tasks on a single thread and schedules them deterministically for testing.
- pron 6y agovar myThreadFactory = Thread.builder().virtual(Executors.newSingleThreadExecutor()).factory(); All threads created by this thread factory will be scheduled to the same kernel thread.
- Cojen 6y agoDoes this example fully answer the question? Does this allow the tasks to be scheduled deterministically? If I'm very careful and write the tasks such that they make blocking calls at specific locations, then yes. Otherwise, is there any feedback to inform me that tasks are switching context at places I didn't expect? Is it possible to define a custom scheduler that can simply print debug messages at every context switch?
- pron 6y agoIt answers the question with a "hello, world." To do more sophisticated stuff, like what you want, you'll need to replace or wrap the standard single-thread Executor with your own Executor. There is exactly one method you need to implement. For example: Executor ste = Executors.newSingleThreadExecutor(); Executor myExecutor = task -> { if (task instanceof Thread.VirtualThreadTask vtt) System.out.println("Scheduling " + vtt.thread() + " on " + Thread.currentThread()); ste.execute(task); if (task instanceof Thread.VirtualThreadTask vtt) System.out.println("Descheduled " + vtt.thread() + " from " + Thread.currentThread()); } var myThreadFactory = Thread.builder().virtual(myExecutor).factory();
- jayd16 6y agoOne headache I see that no one seems to mention is thread affinity seems a lot harder to manage in an implicit system. Many patterns use a single thread for synchronization but often times one thread is special. UI systems often have a UI thread that controls the GLContext or what have you. In something like C#'s async, you can easily schedule tasks off and on that thread. I'm not sure how you could do this implicitly. Loom seems to keep around native Threads for this sort of thing? I would be really interested in seeing a UI system written with Loom. >FFI that bypasses the JDK and interacts with native code that does either IO or OS-thread synchronization is extremely rare in Java. I would also argue its rare because its hard to do. This is a self fulfilling argument. In C#, a very similar language, its much more common to do that sort of thing because its easier. C# is chosen for games and other apps because it can interop with native more easily.
- pron 6y ago> I'm not sure how you could do this implicitly. See https://news.ycombinator.com/item?id=25301246 https://news.ycombinator.com/item?id=25301246 > I would also argue its rare because its hard to do. I would argue it's mostly because the Java ecosystem is so rich (often richer than "native" alternatives), there's hardly ever a need to do it. BTW, as of JDK 16, you'd be able to do FFI in pure Java: https://openjdk.java.net/jeps/389 https://openjdk.java.net/jeps/389 It's not yet entirely convenient -- that would come later.
- vbezhenar 6y ago>>FFI that bypasses the JDK and interacts with native code that does either IO or OS-thread synchronization is extremely rare in Java. >I would also argue its rare because its hard to do. Is it? I recently had to write some simple code with C to interact with WinAPI and JNI was pretty straightforward to do.