15 ms·
We switched to Java 21 virtual threads and got a deadlock in TPC-C for Postgres
- charleslmunger 3y ago"Why not to use java virtual threads" -> Fundamental features of the language treated as implementation details in libraries for more than two decades cause deadlock.
- deleted 3y ago[deleted]
- nottorp 3y agoCan someone explain what these virtual threads are in 2 sentences please?
- ganeshkrishnan 3y agoThey are not real threads. That is, the CPU is not context switching them but jvm running them in async. Usually threads were also used for long running io but not cpu intensive tasks. It's recommended to use virtual threads for such scenarios now
- nottorp 3y agoThanks! > Why two sentences? Maybe you should ask ChatGPT if you want explanations with specific length requirements. As you can see, people can do it better. I put a limit on it because I didn't want an explanation of what threads are, just of the difference.
- marginalia_nu 3y agoThe post you replied to had four sentences. (But two paragraphs).
- nottorp 3y agoThe explanation had two sentences :) Edit: why are some commenters on HN so literal minded anyway? This is free form chat not code specs. You could have read "2 sentences" as "concise", you know...
- xmcqdpt2 3y agoWhy two sentences? Maybe you should ask ChatGPT if you want explanations with specific length requirements. Anyway it's pretty simple really. A generic thread is bunch of stack frames (with their associated local variables). A standard OS thread is under the control of the kernel scheduler which decides whether the thread runs and makes progress or not. The VirtualThread in Java is just a thread which is not directly mapped to the OS thread scheduler but exists as a user space object that can be scheduled by a (Java implemented) scheduler. It's basically just a call stack with its local variables, but one that only steps forward when an OS thread of the scheduler decides to step it.
- dxxvi 3y agoWhy was c3p0 used (its latest version was released in Dec 2019)? Those tests existed for a while and people were too lazy to replace c3p0 with something newer? I guess that they spent all their time to use virtual threads in those tests and had no time left to look at c3p0.
- oldgradstudent 3y agoJava virtual threads did not cause a deadlock here. The deadlock was a usage error. A better title would be: Naively switching to Java virtual threads caused a deadlock in TPC-C for Progress SQL.
- shermantanktop 3y agoArticle won’t load for me. My understanding was that the switch to virtual was supposed to be relatively simple, allowing the programmer to be naive. But a user deadlock is a user deadlock, no matter the threading impl.
- papercrane 3y agoThe issue here is currently virtual threads don't work well with the 'sychronized' keyword. Right now synchronized will pin the carrier thread. The fix was to switch to a higher-level abstraction that works with virtual threads. My understanding is there is work to make synchronized not pin the carrier thread, but that's some pretty complex and important code to change.
- Groxx 3y agoFrom a relatively brief skim and past Go and Java experience: synchronized blocks the current normal thread, so that doesn't really seem any different to me. If you starve your threads, you starve your threads. It definitely leaves room to optimize by not pinning that thread, which would be great, but that shouldn't change semantics at all. Or is there something actually screwed up in the implementation of virtual threads that makes this a much bigger issue?
- extractionmech 3y agoIt’s a thread that supports n virtual threads. You want synchronized in virtual thread a and not the carrier thread which will block all the virtual threads. Been away from Java land for a while. How did something like that even get into release? That’s like a pretty big loaded shotgun to leave lying around with lots of kids playing, no?
- taspeotis 3y agoDid they get a deadlock again? https://news.ycombinator.com/item?id=38939165 https://news.ycombinator.com/item?id=38939165
- eivanov89 3y agoHaha, but quite frankly we had one more in TPC-C for YDB. But unrelated to the virtual threads.
- otterley 3y ago"Our PostgreSQL TPC-C implementation utilizes c3p0 for connection pooling...The problem is that...synchronized code might be deeply embedded within the libraries you use. In our case, it was within the c3p0 library. So, the fix is straightforward: we simply wrapped the connection with a java.util.concurrent.Semaphore. With this change, virtual threads are blocked on the semaphore and, crucially, release the carrier thread instead of delving inside c3p0. Thus, we never block inside c3p0 because we enter c3p0 code only when there is a free session available."
- deleted 3y ago[deleted]
- NovaX 3y agoIt is a known caveat that virtual threads do not work well with long running synchronization by pinning the thread. That unfortunately means that for many applications it may be premature to adopt them, but it is mature enough for broader evaluation by the libraries and frameworks. The Java team provided a status of their efforts recently [1]. https://www.youtube.com/watch?v=WoQJnnMIlFY&t=421s https://www.youtube.com/watch?v=WoQJnnMIlFY&t=421s
- saagarjha 3y agoThe problem is not “long running synchronization” but synchronization that relies on stuff running outside of virtual threads to unblock itself. There is no issue beyond performance if you perform filesystem operations in your mounted state.
- NovaX 3y agoYeah, I wasn’t being particular about this exact issue and was generalizing about synchronization pinning the carrier. A deadlock is trivial once the implications of that are thought through. https://mail.openjdk.org/pipermail/loom-dev/2023-July/005993.html https://mail.openjdk.org/pipermail/loom-dev/2023-July/005993...
- avodonosov 3y agoSorry, the first sentence is a mis-informing wording. The `synchronized` pins the thread only when from within of the `synchronized` the program calls a blocking operation that would normally unmount the virtual thread, like blockingQueue.take() or similar. (Which is not a sane coding practice). It's because the unmounting, as it's implemented today, does not work well with synchronized. It's better if people read JEP 444 than rely on forum comments, to avoid being misinformed. Speaking of long-running - even without synchronized, a long running code keeps the native thread occupied, until some blocking operation is called. So an endless loop that does not call a virtual-thread-ready blocking operation will occupy the native thread forever. Java virtual threads are a kind of cooperative multithreading - another virtual thread only gets chance to kick-in when some current virtual thread reaches specific blocking operations. In contrast to preemptive multi-threading with native threads. So I agree with your conclusion. Virtual threads can not (yet?) be blindly used as a drop-in replacement of native threads for existing code. And the new code needs to take their specifics into account. BTW, another method I discovered to block the native carrier thread that executes a virtual thread is to call blocking reading through FileInputStream, for example reading from the console. The FileInputStream does not implement virtual thread parking at all (yet?).
- hn_throwaway_99 3y ago[flagged]
- FridgeSeal 3y agoI’ve come to see a lot of ai generated images as a proxy for effort, or care, and whenever I see them, my first thought is mostly “the author doesn’t care enough to make an effort about the presentation of their post”. Same reason I stopped using GitHub copilot to write PR bodies, I had a teammate say “I know you didn’t put any effort or thought into writing it,so why should I bother reading it?” Which I resonate with.
- jfengel 3y agoI'm reminded of the way expensive restaurants use very simple descriptions, while less expensive ones load them up with superlatives and (uninformative) adjectives. The high end is confident enough that you assume it will be good. The AI art assumes you need art to think that they've put in effort. More confidence would allow for simplicity. But I cannot say if that confidence would be well placed. For all I know people really do think the art adds class.
- eivanov89 3y agoYour comparison is indeed thoughtful. I would note a counterexample, though: an expensive restaurant with a very simple description gives no guarantee that you will be delighted, while some inexpensive places (possibly with basic ads) might offer something very delightful.
- eivanov89 3y agoI would suggest looking at it from another perspective. Writing a text that precicely describes the image you want might require a lot of effort. Crafting this text to get as close as possible to the imagined picture also requires effort. Also, consider that some people can't draw (I also can't sing), but can write. AI gives a nice opportunity to add some color and visual humor, which is, in my humble opinion, great. But no doubt there are many examples of AI misusage (or overusage), which match your description.
- xyst 3y agoConcurrency, parallelism. These are among the most misunderstood concepts in programming/software development. TLS (especially mutual TLS) and Oauth also join this club.
- papercrane 3y agoCurious if you considered switching to a different connection pooling library. These days I usually use HikariCP which is fast an actively maintained. c3p0 hasn't had any activity for years, I'm not sure if it's still maintained.
- eivanov89 3y agoPerhaps we'll give HikariCP a chance. However, please keep in mind that the goal of the YDB team is to enhance database performance. We needed virtual threads to make TPC-C efficient enough to generate a reasonable load on a modest amount of hardware.
- xmcqdpt2 3y agoIs that true? Virtual threads aren't necessarily faster, you still have just as many sockets and network connections as before. You can easily spawn 5000 platform threads, and if that's not enough, there are quite a few user-space implementations of fibers/coroutines/async etc on the JVM that can deal with many outlying requests (Cats/ZIO in Scala, Kotlin coroutines, the Play framework, concurrent.Future, etc.)
- whalesalad 3y agoBeen ages since I’ve touched it but back in 2017-2018 I had some fun integrating HikariCP in place of c3p0 in some Clojure projects and it was more performant.
- rickette 3y agoIndeed, Hikari is the go to connection pool for some years now. It's even the default when running Spring Boot.
- samus 3y agoCrucially, c3p0 will probably never see the `synchronized` blocks being replaced by reentrant locks. Since LTS offers exist for Java 21, many libraries might actually do that. But I actually hope that the ecosystem resists, which would force virtual thread users suffering from this problem to upgrade soon.
- saagarjha 3y agoThis is a common problem when migrating a system from threads to virtual threads. In general, using primitives which block the current thread and prevent forward progress can quickly lead to deadlocks. It’s a hard issue to catch because in the past usually this would get “solved” by spawning a new thread to complete the task but in a world with virtual threads the runtime is usually reluctant to spawn more threads, so there’s nothing that can service more work if you’ve blocked all the threads.
- Groxx 3y agoIs that all that's happening here? There's an implicit limit on real threads, where before it was unlimited by virtue of not using the virtual thread's limited pool? If it doesn't spawn threads when all of them are blocked, that seems kinda dumb. And a severe change in semantics. It can be conservative and try running unpinned ones on fewer threads and shuffle them around and slowly spawn more to ensure eventual progress, which would mean a possibly significant optimization problem, but a hard cap impacts correctness.
- dboreham 3y agoMy long held belief: green/user-level/M:N threading schemes never work at first, and only work reliably after extreme effort has been put into fixing all the cases where blocking code gets called underneath. afaik there are only two modern working implementations: golang and erlang. This article is consistent with that belief.
- cyberax 3y agoThere are many other implementations, although in less popular languages. The trick is to include the green threads from the start, so there are no libraries that depend on real threading. That's why Go and Erlang are so successful.
- kelnos 3y agoThe funny thing is that Java did have green threads back in v1.1, but they were dropped in v1.3. That doesn't invalidate your point; more than 20 years of Java practice has focused on making things work well for platform threads.
- jake_morrison 3y agoErlang is based on virtual threads (confusingly called processes). The Erlang virtual machine schedules them on OS threads. Erlang processes communicate using message passing, preventing deadlocks. You can use millions of Erlang processes without problems, e.g., to handle millions of Elixir LiveView sessions.
- Jtsummers 3y agoYou can have deadlock in Erlang, it's just a bit harder. It happens when two processes are both waiting on the other to send them a message which is analogous to two threads each waiting for a mutex the other holds. The same thing can happen in Go with its channels, another message passing based concurrency control mechanism.
- jake_morrison 3y agoSure, you can make deadlocks in any language, but it's uncommon in Erlang. Shared state is the exception, and message passing means that things that manage state, such as gen_servers, only process one message at a time from their inbox. Contrast this with languages like Java where every object is a potential concurrency problem. Or the 10+ years of trying to make Python async (see Twisted).
- orthoxerox 3y agoTwo deadlocked processes won't exhaust the thread pool in Erlang, they will simply never wake up.
- toast0 3y agoErlang has the advantage that it was built around processes and is effectively preemptive. Processes can be descheduled anytime they make a function call or use receive to get or wait for messages and being a functional language, you have a finite amount of instructions before calling a function. Other languages adding virtual threads later in life don't have the same ability to feel preemptive. Although I think someone said Java has a nice trick or two? Anyway, if all the virtual threads seem preemptive, you won't have the case that your limited number of actual threads are waiting on locks and not yielding --- all Erlang processes yield eventually; usually in a fairly short time frame.
- dikei 3y agoI wonder if HikariCP, currently the best Java DB Connection Pooling library, suffer the same issue as c3p0.
- deleted 3y ago[deleted]
- schizofunky 3y agoI think i have a more elegant solution for this deadlock: "Switch to haskell".
- shermantanktop 3y agoIf serious: that’s dismissive, superior, and a low-effort appeal to Haskell fans. If not serious: it’s still low effort, but while it is framed as a zinger, it’s not funny at all. I don’t even understand what the humor might be, maybe it’s serious after all.
- mrkeen 3y agoIt would have been better to write: Switch to a language/runtime that's not only had virtual threads for decades, but also a saner synchronisation model (transactions) rather than synchronized blocks.
- kaba0 3y agoEvery language with sufficient concurrency and parallelism primitives are prone to dead locks/live locks and any other kind of race conditions.
- mrkeen 3y agoExactly! It's the same thing with memory management primitives. If you expose malloc and free directly to an application programmer, things will eventually get buggy. It's better to hide the locking primitives and let the runtime handle it for you safely.
- vvern 3y agoGo has a mechanism to spawn a new thread (m in ho runtime parlance) if it thinks one of its threads might be blocked in a cgo (go’s “native function” equivalent). That prevents stuff like this.
- avodonosov 3y agoJava does the same for Object.wait(), only the number of such compensating threads is limited by default, but can be extended via config option. They have exhausted the default number of compensating threads, I think. And they are mistaken to call this situation a "pinning" JEP 444: > The vast majority of blocking operations in the JDK will unmount the virtual thread, freeing its carrier and the underlying OS thread to take on new work. However, some blocking operations in the JDK do not unmount the virtual thread, and thus block both its carrier and the underlying OS thread. This is because of limitations at either the OS level (e.g., many filesystem operations) or the JDK level (e.g., Object.wait()). The implementations of these blocking operations compensate for the capture of the OS thread by temporarily expanding the parallelism of the scheduler. Consequently, the number of platform threads in the scheduler's ForkJoinPool may temporarily exceed the number of available processors. The maximum number of platform threads available to the scheduler can be tuned with the system property jdk.virtualThreadScheduler.maxPoolSize. (In my testing the default ForkJoinPool limit was 256) So theoretically they could have extended the jdk.virtualThreadScheduler.maxPoolSize to a number sufficient for the use case. Although their workaround with semaphores is probably more reliable - no need to guess the sufficient number. The situation with Object.wait() is not what JEP 444 calls "pinning". The "pinning" happens, for example, when one calls `syncronized(....) {blockingQueue.take()}`, which is not sane coding, BTW. In this case the native thread is blocked and is not compensated by another thread - much worse than the Object.wait(). The number of native threads that run virtual threads is equal to the number of CPUs by default, so "pinning" immediately makes one CPU unavailable to the virtual threads of the application. All those issues are temporarily, as I understand. The JDK team works for fix Object.wait(), synchronized, etc.
- delusional 3y ago> The situation with Object.wait() is not what JEP 444 calls "pinning". The "pinning" happens, for example, when one calls `syncronized(....) {blockingQueue.take()}` [...] To call Object.wait() you need to own the objects monitor, which would imply that your code would actually look like `synchronized(....) {Object.wait()}` in which case you would indeed be pinned.
- neonsunset 3y ago[flagged]
- dikei 3y agoYou can code in async/await style in Java too, using `CompletableFuture.supplyAsync` and `CompletableFuture.get` Using Virtual Threads is a choice, it's not forced on to you.
- moonchild 3y agoI am extremely confused. > There are two scenarios in which a virtual thread cannot be unmounted during blocking operations because it is pinned to its carrier: > When it executes code inside a synchronized block or method Isn't 'synchronized' effectively sugar for taking a kind of lock? Why can't it be treated uniformly by the scheduler?
- dikei 3y agoNo, synchronized is a very primitive lock implementation compared to what's available in java.util.concurrent.Locks. However, it's built directly into the JVM specification, so it's difficult to change while keeping compatibility, while j.u.c.Locks is just a library. In other words, they can't change synchronized schematic, so they created j.u.c.Locks as a replacement.
- altfredd 3y ago> it's difficult to change while keeping compatibility Actually, it is trivial to change. Just embed a ReentrantLock into every object and rewrite all calls to "synchronized"/"Object.wait" to use that lock. Unfortunately, this would result in a bit of performance regression (increasing per-object memory footprint). To solve that would require turning ReentrantLock into a magical intrinsic, fully integrated with lock bytes in the object header. Which is actually not that hard either — other runtimes like Golang or Android VM solve problems like this on daily basis. Oracle, however…
- samus 3y ago... was taking years to land Project Loom. So long that people started calling it vaporware. Project Valhalla is still regarded as such by many. It had to be shipped as soon as it was usable, even though a few rough edges remain that really ought to be deburred. As you indicate, the complexity lies in not burning too many bridges with existing users and use cases. This is something that Android regularly does and which Go never really had to do due to its shorter history and up-front design.
- samus 3y ago
- mrintegrity 3y agoTotally off topic but I am getting tired of the AI generated images used on nearly all blog posts nowadays. They are instantly recognisable, it just seems low effort and lowers the feeling of quality one might otherwise have
- azinman2 3y agoTypically it’ll be in lieu of nothing or stock photography. Doesn’t it seem better than that?
- dmart 3y agoNot really. I wish the trend of giant generic hero images on every blog post would go away, they almost never add any value. I think it was Medium that started the trend.
- iknowstuff 3y agoThey do add value, they make clicks more likely.
- logifail 3y ago> They do add value, they make clicks more likely "Making clicks more likely" is a terrible measure of genuine value. There are lots of images which will make people click, even if once they see your page they click 'Back' a second later. Our metrics are broken if we continue to attribute that click as 'success'.
- diggan 3y ago> "Making clicks more likely" is a terrible measure of genuine value. Genuine value, to who? For the author, getting more clicks is probably of "genuine value", depending on their goals for their writing. But seems most people are not writing and publishing stuff today because they think it provides value to others, but because they think it'll provide value to themselves somehow.
- synthetigram 3y agoThis problem is not going to go away so easily. Numerous core Java classes (like BufferedInputStream) use synchronized. I count 1600+ usages in java.base. The blocking issue means it's _much_ easier to accidentally run into this, rather than waving it away as an unlikely edge case. I personally ran into this Using the built in com.sun webserver, with a virtual thread executor. My VPS only has two CPUs which means the FJP that virtual threads run on only have 2 active threads at a time. I ran into this hang when some of the connection hung, blocking any further requests from being processed.
- he0001 3y agoBufferdInputStream is rewritten and is only using synchronized if subclassed. In fact there has been a lot of work removing the synchronized keyword.
- jillesvangurp 3y agoPeople always forget that things that only happen every few million times, can happen fairly frequently on a busy server. This has bitten me numerous times. The nature of a lot of these types of issues is that they are hard to detect and hard to reproduce. Virtual threads are nice for unblocking legacy code but they aren't without issues. There are better options for new code with less trade offs on the jvm as well. I've recently been experimenting with jasync-postgresql (there's a mysql variant as well) as an alternative to JDBC in Kotlin. It's a nice library. It does have some limitations and is a bit on the primitive side. But it appears to be somewhat widely used in various database frameworks for Scala, Java, and Kotlin. Databases and database frameworks are an area on the JVM where there just is a huge amount of legacy code built on threads and blocking IO. It's probably one of the reasons Oracle worked on virtual threads as migrating away from these frameworks is unlikely to ever happen in a lot of code bases. So, waving a magic wand and making all that code non blocking is very attractive. But of course that magic has some hard limitations and synchronize blocks are one of those. I imagine they are working on improving that further.
- kaba0 3y agoSynchronized blocks are not a problem. Synchronized blocks that later don’t unblock the thread may sometimes be.
- spintin 3y agoThe warning shots across the bow where heard with this statement from the devs: "Don't replace platform/native threads with virtual ones, replace tasks (without further explanation) instead"?! Combine that with the fact that they chose to implement the scheduler in Java instead of C(++) and you're set for performance problems. Remember that NIO took from 1.5 to 1.7 to be usable/performant and that was native! Edit: Finally figured out why: https://news.ycombinator.com/item?id=39010648 https://news.ycombinator.com/item?id=39010648
- bitcharmer 3y ago> Combine that with the fact that they chose to implement the scheduler in Java instead of C(++) and you're set for performance problems. Ah yes, the argument from the 1990s. It would make sense to understand where the JVM and its compiler are these days before making incorrect statements about performance. From your link: > Blocking network TCP IO needs a sychronized block to work This is utterly false.
- spintin 3y agoSo how do you implement a TCP socket? I have always had to do synchronized(something) { socketInputStream.read(); } And the dude himself says that reading from a socket is a problem if you listen to the interview.
- bitcharmer 3y agoSomething tells me you don't know much about programming in Java. Just look up any tutorial showing how to use NIO (blocking and non-blocking). Synchronized in this context is pretty nonsensical.
- kaba0 3y ago[flagged]
- MrBuddyCasino 3y ago> they chose to implement the scheduler in Java instead of C(++) and you're set for performance problems The JDK has historically used some native implementations in its stdlib (zip, imageio and others), back when the runtime wasn't as fast as it is today. But today's runtime would often be faster in Java than those native implementations.
- DeathArrow 3y agoWhy dining philosophers from the image have more than two hands?
- isoprophlex 3y agoBecause you're reading a low-effort Medium webshit with AI generated images
- pierrebai 3y agoProbably the author thought that this typical AI-generation quirks were a funny wink to the concept of virtual threads. The elephants, of course, represent Postgres.
- chucke1992 3y agoPersonally I am curious how these features like virtual threads are tested when developed.
- kaba0 3y agoThe model has been extensively tested in TLA+, which can reason about all possible timing combinations, among other things.
- chucke1992 3y agoOh. Interesting. Never heard of it.
- bheadmaster 3y agoWhy couldn't JVM detect when all carrier threads are blocked, and just spawn more of them?
- davidtos 3y agoThat already exists luckily, you can even change the maximum number of carrier threads with: - jdk.virtualThreadScheduler.maxPoolSize=10
- dikei 3y agoThe default is 256, way higher than 10. But of course, when you have thousands of Virtual Threads all deliberately pinning the carrier thread, you quickly run out.
- bheadmaster 3y agoI suppose that a hard limit on the number of carrier threads is a sensible choice then - deadlock is better than creating threads until the system grinds to a halt. But then again, why couldn't scheduler detect a deadlock? Go has a system in place that, in case of total program deadlock, prints out an error message with all goroutines' stack traces, and stops the program. Perhaps Virtual Thread Scheduler could do the same thing? But then again, Java also allows for native threads to run in parallel to Virtual Threads, which makes it impossible to detect whether there's a deadlock, and not just virtual threads waiting on a native thread. I suppose this is a very good example why simple is better than complex.
- hoseja 3y agoBTW, dining philosophers is an extremely clunky example.
- mrkeen 3y agoI was looking to see how it would be relevant, and then: > we present a case study on how we encountered a deadlock with virtual threads in TPC-C for PostgreSQL, even without the dining philosophers problem. I guess it was a clunky non-example! (I was hoping to see a virtual thread solution to compare to: https://www.adit.io/posts/2013-05-15-Locks,-Actors,-And-STM-In-Pictures.html https://www.youtube.com/watch?v=aQXgW55f7cg https://hackage.haskell.org/package/stm )
- karpour 3y ago[flagged]
- mtmail 3y ago20 other commenters already made that observation https://news.ycombinator.com/item?id=39009513 https://news.ycombinator.com/item?id=39009513
- bob1029 3y agoSounds similar to the quirks you get with TPL in .NET under some circumstances. For library code, a ConfigureAwait(false) invoke should be considered to signify that the execution does not need to resume on the original thread.
- torrent 3y agoSeems to be a similiar problem field as writing blocking functions that call async functions in C# and co-existence of synchronous and asynchronous code. There are numerous recommendations such as https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development#the-blocking-hack https://learn.microsoft.com/en-us/archive/msdn-magazine/2015... Final phase is "I hope these techniques will help you adopt async into your existing applications in a way that works best for you."
- pierrebai 3y agoI thought the blog was great but "in summary" conclusion bad. The summary merely stated that Java virtual thread are great. I expected a summary of the problem and solution, for example something like: When using Java 21 virtual threads, you can end-up starved of carrier threads due to all carrier threads waiting on a pool exhausted resources with no thread available to free such resources. The solution is to wrap those resources in a virtual-thread aware object. In our case, we solved our problem by wrapping connections in semaphores.
- skyde 3y agoWhy synchronized block are not preemptible? When compiling public void syncMethod() { synchronized(lock) { // some code } } they could translate to public void syncMethod() { await reentrantLockAsync.lockAsync(); try { await somecodeAsync(); } finally { await lock.unlockAsync() } }
- dikei 3y agoThe first issue is your second code is not Java (no await/async literal for Java yet) The second issue is they're not completely equivalent. In the second case, you'd need extra memory for the `reentrantLock`, while `synchronized` works with any object. Furthermore, if you need to use `wait/notify`, then there need to be an extra `Condition` object to use in combination with the `ReentrantLock`. For sure, developers can rewrite most `synchronized` to use `ReentrantLock` and `Condition`, but javac won't do it automatically for you.
- skyde 3y agoThey could at least introduce a new language construct like await synchronizedAsync(lock) { // some code } C# introduced: await foreach (int item in RangeAsync(10, 3)) Console.Write(item + " "); // Prints 10 11 12 So you dont have to type: IAsyncEnumerator<int> e = RangeAsync(10, 3).GetAsyncEnumerator(); try { while (await e.MoveNextAsync()) Console.Write(e.Current + " "); } finally { if (e != null) await e.DisposeAsync(); }
- jshowalter 3y agoWhy not treat this as a bug, and fix it in Java 21? For compliance reasons we can only use LTS versions, and the next one isn't until September 2025, according to https://www.oracle.com/java/technologies/java-se-support-roadmap.html https://www.oracle.com/java/technologies/java-se-support-roa....