[HN Gopher] Java 21: The Nice, the Meh, and the Momentous
       ___________________________________________________________________
        
       Java 21: The Nice, the Meh, and the Momentous
        
       Author : pwpwp
       Score  : 204 points
       Date   : 2023-09-22 15:02 UTC (7 hours ago)
        
 (HTM) web link (horstmann.com)
 (TXT) w3m dump (horstmann.com)
        
       | billfruit wrote:
       | Does it add stdint style names for integer types, unsigned
       | integer types etc?
        
         | layer8 wrote:
         | The size of the integer types are already fixed by the JVM
         | specification ( _int_ is always 32 bits, etc.), and there are
         | no unsigned integer types in Java except for _char_ (a 16-bit
         | unsigned integer type). Furthermore, Java does not support
         | alias names for types. Hence it's unclear what your question is
         | aiming at.
        
         | szatkus wrote:
         | AFAIK Java 8 added a few methods that helps you handle integers
         | as if they were unsigned, like `toUnsignedString`. I think it's
         | enough for any exotic cases.
        
       | PaulHoule wrote:
       | (1) It's a bit of a bad smell (which he points out) that records
       | aren't being used much at all in the Java stdlib, I wrote
       | something that built out stubs for the 17 and 18 stdlibs and that
       | stood out like a sore thumb. I do like using records though.
       | 
       | (2) I've looked at other ways to extend the collections API and
       | related things, see
       | 
       | https://github.com/paulhoule/pidove
       | 
       | and I think the sequenced collections could have been done
       | better.
       | 
       | (3) Virtual Threads are kinda cool but overrated. Real Threads in
       | Java are already one of the wonders of the web and perform really
       | well for most applications. The cases where Virtual Threads are
       | really a win will be unusual but probably important for somebody.
       | It's a good thing it sticks to the threads API as well as it did
       | because I know in the next five years I'm going to find some case
       | where somebody used Virtual Threads because they thought it was
       | cool and I'll have to switch to Real Threads but won't have a
       | hard time doing so.
        
         | yankput wrote:
         | Virtual threads are strictly better than normal threads, no? I
         | am thinking of any reason to still use traditional threads. Is
         | there any downside?
        
           | papercrane wrote:
           | Currently virtual threads aren't a good match if you have a
           | CPU heavy workload. The scheduler isn't fair and if your code
           | doesn't enter into any blocking code it won't be unmounted
           | from the carrier thread.
        
             | yankput wrote:
             | Ahh. It makes sense. But it's much better fit for file
             | io/sockets/db.
        
         | papercrane wrote:
         | I suspect if we had records from the start they'd be all over
         | the stdlib, but because of backwards compatibility they'll
         | likely only be considered for new APIs.
        
         | twic wrote:
         | I think the biggest impact of virtual threads is that the
         | ecosystem will abandon asynchronous APIs. No more futures,
         | callbacks, servers where you have to make sure not to block the
         | thread, reactive frameworks, etc. Just nice simple imperative
         | blocking code. Nima is the first example i've seen:
         | 
         | https://helidon.io/nima
         | 
         | We've had two production bugs in the last two weeks caused by
         | handlers blocking the server thread in apps using an async web
         | framework, which would simply not have happened with a
         | synchronous server.
        
           | jayd16 wrote:
           | You'll still have the structured concurrency calls but that's
           | much better than pure callback hell.
        
         | zmmmmm wrote:
         | I think virtual threads are huge.
         | 
         | The problem with regular threads is (a) multi-kb memory stack
         | per thread and (b) consuming a file handle.
         | 
         | Either of those severely limits the scalability of the most
         | "natural" parallelism constructs in Java (perhaps generally).
         | Whole classes of application can now just be built "naturally"
         | where previously there were whole libraries to support it
         | (actors, rxJava, etc etc).
         | 
         | It make take a while for people to change their habits, but
         | this could be quite pervasive in how it changes programming in
         | general in all JVM languages.
        
       | logicchains wrote:
       | Does anyone know if Java virtual threads will also have channels
       | and a select concept, like in Go?
        
         | aardvark179 wrote:
         | I think at some point yes. We certainly discussed it but it's
         | one of those things that takes time to really get right and
         | performant.
        
       | mrkeen wrote:
       | In the code example for virtual threads, I have no idea what will
       | happen in parallel.
       | 
       | How do I reason about the order in which the calls change the
       | state of the world?
        
         | Jtsummers wrote:
         | That's all sequential code, it would be run inside a single
         | "virtual thread". Note that the async code on the right is also
         | sequential, just structured through an async API.
        
           | Svenskunganka wrote:
           | From my perspective they're not entirely equivalent. The
           | async variant seems to be batching getImages and saveImages,
           | while the sync variant gets and saves each image
           | individually, sequentially.
        
             | Jtsummers wrote:
             | They aren't perfectly equivalent because the virtual thread
             | example uses a loop instead of the following (dropping the
             | try/catch):                 // client.sendAsync(request,
             | HttpResponse.BodyHandlers.ofString())       var response =
             | client.send(request, HttpResponse.BodyHandlers.ofString());
             | // .thenApply(HttpResponse::body)       var body =
             | response.body();       // .thenApply(this::getImageURLs)
             | var urls = getImageURLs(body);       //
             | .thenCompose(this::getImages)       var images =
             | getImages(urls);       // .thenAccept(this::saveImages)
             | saveImages(images);
             | 
             | And if it had been written this way it would have been
             | clearer that they are, in fact, equivalent. But generally
             | people don't write like this, they use looping constructs.
             | 
             | Regardless, the important bit is that the
             | parallel/concurrent bit of the async one is that it is cast
             | off into an async system. The following execution steps
             | are, well, steps. Each executed in sequence. Just like the
             | body of the virtual thread example would be executed, but
             | without the cumbersome noise of _thenApply_ and
             | _thenCompose_ and such.
        
       | Someone1234 wrote:
       | If you're viewing that website on a desktop, I strongly suggest
       | removing max-width: 90ch from the body css. Instead of 50% white
       | space, it goes full width and makes the table substantially more
       | readable (particularly the code samples).
        
         | munk-a wrote:
         | Hilariously enough I was initially confused by this comment
         | because the webpage rendered so readably for me - the base CSS
         | is actually quite reasonable and because I have JS disabled by
         | default the page never re-rendered into the thinner mode.
        
           | Someone1234 wrote:
           | It may be my specific setup. But on a 1440p display, 125% OS
           | scale, I'm seeing more white left/right than actual content
           | in the middle. It is also wrapping the code making it
           | difficult to read.
           | 
           | Completely readable at 100% width though.
        
       | anonymousDan wrote:
       | Can anyone explain this comment: "In the past, a thread pool
       | didn't just throttle the incoming requests but also the
       | concurrent resources that your app consumed. If you now accept
       | many more incoming requests, you may need other ways to manage
       | resource consumption."
        
         | yCombLinks wrote:
         | Yeah, if your server maxed out at 256 system threads you didn't
         | have to worry about the fact that 1024 simultaneous calls would
         | crash your DB. But now you're not limited by system threads
        
         | [deleted]
        
       | hinkley wrote:
       | What's the Scala community think about this development? I would
       | think this would affect them quite a lot.
       | 
       | Google is not helping.
        
         | discodachshund wrote:
         | The Typelevel folks on Discord are of the opinion it's not of
         | much interest to them
        
       | Vicinity9635 wrote:
       | The examples having to word wrap in a tiny text box look even
       | more absurd and unreadable when the page is only using 1/3rd of
       | the screen.
       | 
       | What is with this awful formatting?
       | https://i.imgur.com/nQmt7Qo.png
        
       | marginalia_nu wrote:
       | > Miscellaneous new methods -- meh
       | 
       | Dunno, several of these are tangible QoL boosts:
       | 
       | Math.clamp(), List.reversed(), List.addFirst(), List.addLast(),
       | Character.isEmoji()
        
         | bcrosby95 wrote:
         | > List.reversed(), List.addFirst(), List.addLast()
         | 
         | These fall under sequenced collections, not "miscellaneous new
         | methods".
        
           | marginalia_nu wrote:
           | I guess? I found them under the API diff linked as
           | "miscellaneous new features".
        
       | waynesonfire wrote:
       | > Over 10,000 bug fixes
       | 
       | Most of which were likely introduced during new feature
       | development in recent releases. To suggest that this on its own
       | somehow manifests a more stable jdk compared to some ancient,
       | battle tested version of the jdk is debatable.
       | 
       | I find it rather concerning that so many bugs exist to begin
       | with. Why are these not caught sooner?
       | 
       | Has the whole world gone crazy? Am I the only one around here who
       | gives a shit about quality? Mark it zero!
        
         | specialist wrote:
         | Randomly looking at bugs fixed the last 10 weeks, it seems like
         | a healthy mix of old and new bugs.
         | 
         | https://bugs.openjdk.org/browse/JDK-8316305?filter=-7&jql=pr...
         | 
         | Being allergic to JIRA, my JIRA-fu is weak, so there's probably
         | an easier/faster way to report bugs fixed in v21.
         | 
         | Any way.
         | 
         | > _Am I the only one around here who gives a shit about
         | quality?_
         | 
         | Ages ago, I was a QA/Test manager. So I appreciate your
         | sentiment. But it seems to me that Oracle's being a _FANTASTIC_
         | shepherd of Java. Definitely a huge upgrade, at the very least.
        
         | doodpants wrote:
         | You might be the only person in the world who writes bug-free
         | code on the first try.
        
       | Vicinity9635 wrote:
       | [flagged]
        
       | ecshafer wrote:
       | Java getting better pattern matching is a great change. Id really
       | like more of the functional features to make it into Java.
       | 
       | I would love if Java pattern matching could at least get to the
       | level of ruby pattern matching. Ruby pattern matching will allow
       | you to deconstruct arrays and hashes to get pretty complicated
       | patterns, which is really powerful. Right now it seems like Java
       | might have that with a lambda in the pattern, but its not going
       | to be as elegant as ruby where:
       | 
       | case {name: 'John', friends: [{name: 'Jane'}, {name: 'Rajesh'}]}
       | in name:, friends: [{name: first_friend}, *] "matched:
       | #{first_friend}" else "not matched" end #=> "matched: Jane"
       | 
       | But the big change here is virtual threads which should be a game
       | changer.
        
         | unregistereddev wrote:
         | Pattern matching is a neat tool to keep in the toolbox. When
         | it's the right tool for the job, it is really cool and is a lot
         | cleaner than a bunch of conditional checks. However, I rarely
         | reach for it. Maybe my use cases are unusual? I am genuinely
         | curious how often other developers find pattern matching to be
         | the best tool for the job.
        
           | weatherlight wrote:
           | In languages that have strong support for pattern matching,
           | whether it be on values or types, I find myself reaching for
           | it instead of conditionals. It's all about the explicitness
           | for me. You have to list out all the cases you care about, so
           | there's no room for ambiguity. Plus, the compiler will
           | usually warn you if you've missed a case, which is like a
           | built-in bug catcher. It's also great for working with
           | immutable data, less state to worry about. And let's talk
           | about readability; the code basically documents itself
           | because you can see the shape of the data right in front of
           | you. You can even destructure data on the fly, pulling out
           | exactly what you need. If you're using a statically-typed
           | language, pattern matching adds an extra layer of type
           | safety. And, not to forget, it nudges you toward a more
           | functional style of coding, which I find leads to cleaner,
           | more modular code. So yeah, I reach for pattern matching
           | quite a bit; it often feels like the right tool for the job.
        
           | grumpyprole wrote:
           | One example for you: anytime you needed to use the "Visitor
           | pattern" to do a transformation from one representation to
           | another - you don't need it now. Sealed classes and pattern
           | matching will be more succinct and easier to reason about.
        
           | ecshafer wrote:
           | I think that you can replace almost any If else with pattern
           | matching. Pattern matching makes type checks easier, which if
           | you are really heavily using types through your program,
           | makes pattern matching even better.
        
           | hibikir wrote:
           | Pattern matching is what makes sum types ergonomic enough to
           | be used. Many a Java design doesn't use said interface-based
           | sum types because it's so cumbersome to use them. But whena
           | language has pattern matching, then suddenly designing with
           | sum types in mind is done a lot, and therefore you see
           | examples of good pattern matching everywhere.
           | 
           | When I teach Scala, a very high percentage of the teaching
           | time is ultimately down to re-introducing how to design
           | business domains, because seasoned devs just reach for large
           | classes with a million optional fields, which not only can
           | represent valid systems states, but thousands of invalid
           | ones.
        
           | bcrosby95 wrote:
           | It probably depends on the language you're using. Pattern
           | matching is awesome in Erlang and Elixir. In most other
           | languages it ranges from "nice" to "bleh".
        
             | nayuki wrote:
             | Pattern matching is awesome in Rust. It carries the stellar
             | legacy of Haskell.
        
           | Jtsummers wrote:
           | When available, I pretty much always use pattern matching. It
           | tends to shorten code while not reducing clarity (often
           | increasing it) which means fewer opportunities for errors to
           | creep in. Statically typed languages that can detect
           | incomplete case handling also reduces the chances for some
           | errors (as long as you don't make a catch-all case) but also
           | helps when you change something so that a new case is needed.
           | It also tends to shift the code to the left, reducing the
           | indentation. So shorter, clearer, less unnecessary
           | indentation. Generally a positive.
        
         | frou_dh wrote:
         | I really like that Ruby throws NoMatchingPatternError if none
         | of the patterns match. It's a bit like the much-acclaimed
         | exhaustive pattern matching in static languages (though at
         | runtime rather than compile-time, obviously) and better than
         | just silently falling off the end, which IIRC is what Python's
         | pattern matching does.
        
           | rusk wrote:
           | In Python you can terminate a for loop with else, which will
           | be run whenever the loop runs to the end without breaking
        
             | specialist wrote:
             | Neat. Will check it out.
             | 
             | I recently spotted a (new to me) foreach / else construct
             | in a templating language (sorry, forget which one); else is
             | invoked if the list is empty. Nice sugar for common outputs
             | like "no items found".
             | 
             | I appreciate modest syntactic sugar.
             | 
             | For instance, my #1 sugar wish is for Java's foreach is to
             | do nothing when the list reference is null. Versus tossing
             | a NPE.
             | 
             | Eliminates an unnecessary null check and makes the world a
             | little bit more null-safe.
        
         | munificent wrote:
         | We recently added pattern matching to Dart [1], so I'm always
         | keen to see how it compares to similar features in other
         | languages. In case it's interesting, here's that Ruby example
         | ported to Dart:                   print(switch ({'name':
         | 'John', 'friends': [{'name': 'Jane'}, {'name': 'Rajesh'}]}) {
         | {'friends': [{'name': var firstFriend}, ...]} => "matched:
         | $firstFriend",           _ => "not matched"         });
         | 
         | Pretty similar! The main differences are that Dart doesn't have
         | symbols, so the keys are string literals instead. Also,
         | variable bindings in patterns are explicit (using "var") here
         | to disambiguate them from named constant patterns.
         | 
         | [1]: https://medium.com/dartlang/announcing-dart-3-53f065a10635
        
           | brabel wrote:
           | > We recently added pattern matching to Dart [1]
           | 
           | I've been using that and I love it, in general... but can I
           | ask you why do we need to name a variable in a pattern like
           | this:                   switch (p) {           Person(name:
           | var name) => ...         }
           | 
           | That's the only thing that feels a bit annoying as you have
           | to rename the variable... In Java, this would be something
           | like:                   Person(var name) -> ...
           | 
           | EDIT: I guess it's to support `Person(name: 'literal')`
           | matches.
           | 
           | > Dart doesn't have symbols
           | 
           | That's weird, as I actually use sometimes `#sym` (which has
           | type `Symbol`)??                   print((#sym).runtimeType);
           | 
           | This prints `Symbol` :)
           | 
           | I know you know Dart in and out, but could you explain why
           | this is not actually a symbol in the way Ruby symbols are?
        
         | brightball wrote:
         | Simple solution: JRuby.
         | 
         | Virtual threads are going to make Ruby fibers work properly for
         | JRuby so that's going to be huge as well.
         | 
         | Charles Nutter gave an update in August. 45 minute mark he
         | talks about virtual threads.
         | 
         | https://youtu.be/pzm6I4liJlg?si=vKVICrola4OmJIal
        
       | adra wrote:
       | Virtual threads are going to be great, but they're still limited
       | (still starved the pool when used with 'synchronized' blocks),
       | and they aren't the structured concurrency power houses like
       | kotlin coroutines, but its an invaluable tool that will only
       | continue to accelerate as the ecosystem moves to adopt them.
       | 
       | Expect a lot of libraries to start release versions that are java
       | 21 baseline because of this feature alone. We're in for a little
       | bit of dependency hell for the short while. Thankfully, devs have
       | been exposed to a mostly final loom for a year, so my hope is
       | that at least the big projects are well on their way to quick
       | adoptions.
       | 
       | Unlike the 8->11 migration which largely brought pain, the 8->21
       | release brings with it a ton of value that i think will encourage
       | most shops to actually pull the trigger and finally abandon 8.
        
         | skwirl wrote:
         | Do you have to baseline on Java 21 if you want to add support
         | for virtual threads? Couldn't you continue using heavyweight
         | threads on older versions of Java? My understanding is that
         | both use the same Thread abstraction.
        
           | adra wrote:
           | From an API perpective, you can always use reflection to
           | cheat past the option to create virtual threads in pre-21
           | (without previews) java bytecode, but you need to do more to
           | your code than just flip the switch to support virtual
           | threads.
           | 
           | A virtual thread thread pool by definition is unbound. If
           | you're binding data to a thread (eg. Thread locals, you now
           | have a seemingly unbound list of threads that is now
           | effectively a memory leak). I bumped into that one a few
           | months ago with Netty that has a per thread cache for some
           | things (thankfully you can turn off that cache). It was
           | creating a significantly large waste of RAM that slowed down
           | the application alone.
           | 
           | The other big one is as I mentioned the synchronized
           | limitation. If you assume naively that anything can run in a
           | virtual thread without worries, you're opening yourself up to
           | deadlocks or at least significantly low performance code if
           | you're relying on libraries/code that are synchronized using
           | java monitors.
           | 
           | There may be more examples of gotchas, but these two are the
           | most notable examples I have right now.
        
           | Quekid5 wrote:
           | I believe, e.g. ZIO 2.next is doing something like this,
           | dynamically deciding whether running something async or just
           | doing the blocking thing depending on the availability of
           | VThreads... but of course that's Scala, so YMMV.
           | 
           | Without a way to trampoline computation (or transform code
           | appropriately) it's probably impractical to do anything like
           | that.
           | 
           | (And of course, still many caveats as the sibling post points
           | out.)
        
         | larperdoodle wrote:
         | Why haven't places updated already? It's not that much work to
         | update. Where I work we always go to the new LTS version as
         | soon as it's supported by gradle.
         | 
         | Doesn't cross anyone's mind to _not_ upgrade.
        
           | yCombLinks wrote:
           | The services I work on pump the entire business revenue from
           | start to finish. A few nice to haves for devs aren't any
           | where close in the risk calculation if something breaks
        
           | Brystephor wrote:
           | 1) dependencies need to be upgraded. for example, not all
           | versions of Gradle support all Java versions. So you need to
           | upgrade Gradle to upgrade Java.
           | 
           | 2) other things are deemed to have higher priority.
           | 
           | 3) people are satisfied with existing features and don't want
           | to spend energy to upgrade to something that doesn't provide
           | immediate value.
           | 
           | 4) folks aren't educated on what the benefit of switching
           | would be so why would it be prioritized? This is a case of
           | "they don't know what they don't know".
           | 
           | I work on a team using Java 8 daily. It's fine. It's got
           | things I wish it didn't (no null in switch statements for
           | example) but I don't care about that so much that I'm going
           | to go through the pain of upgrading 7-9 services in the mono
           | repo, their dependencies, and then test them all to be on a
           | new version of Java.
        
             | krzyk wrote:
             | Gradle/groovy is a liability for any jdk upgrades
             | (similarly like lombok, but it usually supports new JDK at
             | release, not before).
             | 
             | We ditched spock because of groovy, and never looked back.
             | Now at jdk 21, previously at 20.
        
               | brabel wrote:
               | That's pretty disingenuous. Groovy has always run on
               | newer JDK versions before they even get released.
               | 
               | One year ago, Gpars already supported Virtual Threads:
               | https://groovy.apache.org/blog/gpars-meets-virtual-
               | threads
               | 
               | As a heavy user of Groovy/Spock, though, I agree that
               | upgrading Groovy itself can be challenging,
               | unfortunately. Really depends though on how many edgy
               | Groovy features you relied on :).
        
               | krzyk wrote:
               | Not always for sure. We started JDK upgrades with 9 and
               | went +1 every half year and Groovy was lacking with one
               | of 10, 11, 12 or 13. It got so tiring that we had to let
               | it go. Fortunately our tests were mostly JUnit 5, so it
               | wasn't much of work.
               | 
               | We only used it for Spock AFAIR.
        
           | defatigable wrote:
           | This is a niche case, but I spent months trying to upgrade
           | one of our services from one LTS version to the next (I
           | forget which). We encountered a weird bug where services
           | running on the latest JRE would mysteriously corrupt fields
           | when deserializing thrift messages, but only after running
           | for a little while.
           | 
           | After an enormously unpleasant debugging cycle, we realized
           | that the JIT compiler was incorrectly eliminating a call to
           | System::arrayCopy, which meant that some fields were left
           | uninitialized. But _only_ when JIT compiled, non-optimized
           | code ran fine.
           | 
           | This left us with three possible upgrade paths:
           | 
           | * Upgrade thrift to a newer version and hope that JIT
           | compilation works well on it. But this is a nightmare since
           | A) thrift is no longer supported, and B) new versions of
           | thrift are not backwards compatible so you have to bump a lot
           | of dependent libraries and update code for a bunch of API
           | changes (in a LARGE number of services in our monorepo...).
           | With no guarantee that the new version would fix the problem.
           | 
           | * File a bug report and wait for a minor version fix to
           | address the issue.
           | 
           | * Skip this LTS release and hope the JIT bug is fixed in the
           | next one.
           | 
           | * Disable JIT compilation for the offending functions and
           | hope the performance hit is negligible.
           | 
           | I ultimately left the company before the fix was made, but I
           | think we were leaning towards the last option (hopefully
           | filing a bug report, too...).
           | 
           | There's no way this is the _normal_ reason companies don 't
           | bump JRE versions as soon as they come out, but it's happened
           | at least once. :-)
           | 
           | In general there's probably some decent (if misguided) bias
           | towards "things are working fine on the current version, why
           | risk some unexpected issues if we upgrade?"
        
             | dihrbtk wrote:
             | did you work for a very large rideshare company by any
             | chance?
        
           | jfengel wrote:
           | The bigger the project, the more painful the upgrade. Package
           | systems are convenient to avoid reinventing the wheel, until
           | you have to upgrade any piece of it. Then you're stuck trying
           | to figure out which versions of each package go together.
           | 
           | If Package A won't run on JDK 17 your entire project is stuck
           | on JDK 11. If Package B is upgraded but has conflicts with
           | Package A, you have to dig through old versions until you
           | find one that works -- and you don't get upgrades.
           | 
           | The more games somebody has played with reflection,
           | undocumented features, deprecations, etc. the more likely you
           | are to have a conflict. And since package managers encourage
           | you to depend on somebody else's code, you end up depending
           | on _everybody_ else 's code.
           | 
           | The smaller and greener the project is the more likely it is
           | you can just pull the latest versions and be happy about it.
           | A project that was written when Java 8 was current, and
           | continued to develop, is going to be a nightmare.
        
             | Macha wrote:
             | "Oh look, I need to upgrade mockito and Spring. Oh, now I
             | upgraded Spring I need to update the spring JPA plugin. Oh
             | now I upgraded that I need to upgrade Hibernate. Oh now I
             | need to upgrade the library built on it that that team over
             | there maintains. Oh, they're not interested." etc. etc.
        
           | [deleted]
        
         | krzyk wrote:
         | You forgot about 8->17 which ads really nice language features,
         | records alone are greatest feature after lambdas.
         | 
         | And 21 brings patterns in switch and records.
        
         | brabel wrote:
         | > Expect a lot of libraries to start release versions that are
         | java 21 baseline because of this feature alone.
         | 
         | Java has had multi-version jars since 11 I think... that allows
         | library authors to ship code that benefits from new features in
         | newer versions of the JDK while still supporting older ones as
         | well. Hopefully library authors can leverage that, though I'm
         | aware something like Virtual Threads may be very difficult to
         | design around for older versions.
        
         | tantamounta wrote:
         | With the API being nearly the same, I keep just thinking that
         | Virtual Threads are basically identical to Platform Threads
         | except that they use far less memory (so you can have lots more
         | of them).
         | 
         | Are there any other actual differences? Better Peformance?
        
           | noelwelsh wrote:
           | The context switch time is much smaller, so yes, better
           | performance.
        
           | pron wrote:
           | The relationship between throughput, latency, and concurrency
           | in servers is expressed via Little's theorem. If your server
           | is written in the thread-per-request style -- the only style
           | for which the platform offers built-in language, VM, and
           | tooling support -- then the most important factor affecting
           | maximum throughput is the _number_ of threads you can have
           | (until, of course, the hardware is fully utilised). Being
           | able to support many threads _is_ the most effective
           | improvement to server throughput you can offer.
           | 
           | See: Why User-Mode Threads Are Good for Performance
           | https://youtu.be/07V08SB1l8c
        
             | Svenskunganka wrote:
             | I watched the video and thoroughly enjoyed it, thank you
             | for sharing it! I have a question that is perhaps not
             | entirely related to the video, but it touches the topic of
             | context switches. I've read this post [1] by Chris Hegarty,
             | which explains that when calling the traditionally blocking
             | network I/O APIs in the Java stdlib from a virtual thread,
             | it uses asynchronous/poll-based kernel syscalls (IOCP,
             | kqueue, epoll on Windows, Mac and Linux respectively) which
             | I assume is to avoid blocking the carrier threads. That
             | post was written in 2021, does it still hold true today in
             | Java 21?
             | 
             | Reading that, it also makes me wonder what happens for disk
             | I/O? Many other runtimes, both "green thread" ones like
             | Golang and asynchronous like libuv/tokio, use a blocking
             | thread pool (static or elastic) to offload these kernel
             | syscalls to because, from what I've read, those syscalls
             | are not easily made non-blocking like e.g epoll is. Does
             | Java Virtual Threads do the same, or does disk I/O block
             | the carrier threads? For curiosity, does Java file APIs use
             | io_uring on Linux if it is available? It is a fairly
             | recently added kernel API for achieving truly non-blocking
             | I/O, including disk I/O. It doesn't seem to bring much over
             | epoll in terms of performance, but has been a boon for disk
             | I/O and in general can reduce context switches with the
             | kernel by reducing the amount of syscalls needed.
             | 
             | [1]: https://inside.java/2021/05/10/networking-io-with-
             | virtual-th...
        
             | tantamounta wrote:
             | Thanks for the video. I feel like there's a bit of
             | conflation between the terms "performance(latency)" and
             | "throughput", but I see the point. I'd be interested to see
             | that latency graph (Time marker 15:38) between platform and
             | virtual threads in the case where the server doesn't
             | manufacture a 100ms delay (say, in the case of a caching
             | reverse-proxy).
             | 
             | Also - millions of Java programmers thank you for not going
             | to async/await. What an evil source-code virus (among other
             | things that is).
             | 
             | I tried to watch it at 1.25x speed as I normally do, but
             | you already talk at 1.25x speed, so no need !
        
               | pron wrote:
               | To understand what happens when the server doesn't
               | perform IO, apply Little's formula to the CPU only.
               | Clearly, the maximum concurrency would be equal to the
               | number of cores, which means that in that situation there
               | would be no benefit to more threads than cores. What you
               | would see in the graph would be that the server fails
               | once L is equal to the number of cores. The average ratio
               | between IO and CPU time as portions of the average
               | duration would give you an upper limit on how much more
               | throughput you gain by having more threads. That's what I
               | explain at 11:34.
               | 
               | Also, both throughput and latency are performance
               | metrics.
        
         | pron wrote:
         | Structured concurrency in JDK 21 is not only a powerful and
         | flexible library feature, but one that is built deep into the
         | runtime in a way that allows observability into the
         | relationships among threads: https://openjdk.org/jeps/453
        
           | logicchains wrote:
           | Do you know if clojure will be adopting these virtual
           | threads, or still using the macro-based approach?
        
             | [deleted]
        
           | cogman10 wrote:
           | It's somewhat unfortunate that structured concurrency ended
           | up being a preview feature in 21. I agree that it's a great
           | addition but man it'd be nice if it made the LTS.
           | 
           | As it stands, probably won't be heavily used until Java 25.
        
           | [deleted]
        
           | Quekid5 wrote:
           | Congrats to you and the team on this huge milestone!
           | 
           | Really looking forward to taking advantage of these things
           | (transparently and automatically!) in ZIO/Scala... which I
           | think shows the true power of the JVM-as-platform approach
           | you're taking!
        
       | baq wrote:
       | > "Hello, World!".splitWithDelimiters       >      ("\\pP\\s\*",
       | -1)       >  // ["Hello", ", ", "World", "!", ""]
       | 
       | > Meh
       | 
       | My brain just melted.
        
         | hinkley wrote:
         | I'd have a lot of uses for that. But also worry about it
         | enabling more stringly-typed code.
        
       ___________________________________________________________________
       (page generated 2023-09-22 23:01 UTC)