3 ms·
Wrangling executor services (practically) becomes an antipattern. Where old java you'd do a singleton threadpool, virtual thread java you'd pretty much never d
by cogman10 3y ago
Wrangling executor services (practically) becomes an antipattern. Where old java you'd do a singleton threadpool, virtual thread java you'd pretty much never do that. In cases where you might be tempted to do that, the common fork join pool is likely the better choice.
Locks become more relevant. Old java you might have limited concurrency using thread pool size. With virtual threads something like a blocking queue or semaphore is likely a better choice.
Reactive java should be avoided (IMO). It was a decent choice for old java, for virtual thread java you get little benefits from it.
This will probably change, but with old java `synchronized` was alright. Virtual thread java you want to avoid that.
This likely won't change, ThreadLocalStorage is a bad idea with virtual threads, you almost certainly want to use ScopedValues instead.
You should understand the implications of structured concurrency. It's not fully ready yet, but at very least the `try(var service = newVirtualThreadPoolExecutor)` does work (I believe) which will block until all tasks launched are completed. This is likely desirable but might be unexpected if you wanted to fire and forget something.
Otherwise, I think all the old concurrency advice applies.
- clumsysmurf 3y ago> Reactive java should be avoided (IMO). It was a decent choice for old java, for virtual thread java you get little benefits from it. This is interesting, we used RxJava quite a bit on Android until Kotlin Coroutines made things a bit easier in that the code read more linearly and worked nicely with built in language features like exceptions for error handling. Just curious if you agree with this in Java, or had other reasons.
- vips7L 3y agoEven before the introduction of virtual threads RxJava should have been avoided. It colors your entire code base, prevents natural error handling, and quite frankly makes maintenance that much more complex. Obviously there’s a place for it, but every project I’ve been on that used it shouldn’t have.
- Alupis 3y agoI'm curious about this from the perspective of a functional Kotlin backend (using Arrow or not). Do virtual threads make reactive within that model unnecessary too? Seems maybe not? Reactive is both a model, and a paradigm, no? Even with virtual threads, there will still be a reason to write reactive programs it seems.
- prpl 3y agoI think it's (looesely) tasks vs threads. Threads have a stack trace and a well defined state in the JVM, tasks are objects on the heap. I work on moderate sized async reactive pipeline with lots of CompletableFutures throughout (and chaining of them so you can process a batch in parallel and commit contiguous offsets periodically) and there's several bugs that have been undebuggable because they happpen _infrequently enough_ in prod, the heap dump is huge, and there's no stack trace to tell you what is going on. The best you can do is lots lots and lots of logging and turn it into an analytics problem to find out what happened (to be fair, logging is only marginally better than wading through a heap dump of completable futures).
- deleted 3y ago[deleted]
- cogman10 3y agoThere's going to be a difference in the way you do things. Most of the completable future API is going to be unnecessary, .join will work better as there's not penalty for blocking. I've made my fair share of completable future messes with a lot of biconsume/thenApply/handle nonsense to try and avoid blocking.
- netheril96 3y agoIt will be years when most Android users are on Java 21. It’s also not a feature that can be transpiled. So for Android developers virtual threads are probably not a thing we need to understand or use in any near future.
- cogman10 3y agoThe reason to prefer kotlin coroutines is exactly the reason to ditch reactive for virtual threads. The only difference is virtual threads have platform support which means even better stack traces and no need to decorate your method. So yup, agree.
- _ink_ 3y agoWhy stop using 'synchronized'?
- dikei 3y ago`synchronized` is a lock without any ordering/fairness guarantee. Therefore, with the increased concurrency caused by Virtual Threads, there's higher chances for starvation. `ReentrantLock` pretty much replaces every use case of `synchronize` and also supports `fairness`. The only downside of `ReentrantLock` is making the code more verbose, and inexperienced programmers might forget to release the lock.
- mattbee 3y agoMore than that (as referenced above), `synchronized` currently pins a virtual thread to its carrier. So if you had 4 carrier threads, you only need to have 4 synchronized methods in flight that depend on the action of a 5th to hang the JVM.
- dikei 3y agoGot it, so for now using `synchronized` actually prevents the concurrency benefits of virtual thread because it pins the carrier thread. Plus, there's also the possibility of deadlock when mixing `synchronized` and `j.c.u.Lock`.
- cogman10 3y agoPretty much. You won't die if you run over some synchronized code (like ConcurrentHashMap) but you do have to think about what's happening when you hold that lock. From the mailing list it sounds like the plan is to fix this, I hope that happens sooner rather than later.