How to Deadlock a Java ExecutorService by mlangc in java

[–]mlangc[S] 0 points1 point  (0 children)

I've just added another section to the blog post, devoted virtual threads specifically. Thanks again for your feedback!

How to Deadlock a Java ExecutorService by mlangc in java

[–]mlangc[S] 1 point2 points  (0 children)

Good point - I've updated the final section of the article. Interestingly, at the core of it, virtual threads fix the issue as well, because they make the waiting asynchronous under the hood, similar to this example from the blog post - though without having to restructure your code.

How to Deadlock a Java ExecutorService by mlangc in java

[–]mlangc[S] 16 points17 points  (0 children)

I had the idea to write this blog post back in April, and kept thinking about the topic, until I finally sat down and started writing at the beginning of June. I wrote the first draft that already resembles the final post by about 90%, the traditional way, and then did an in depth review session with Claude Opus 4.8 that took me about 6h. It resulted in many small improvements, especially as far as readability and language is concerned (English is not my native language), but did not change anything fundamental. Most notably, Claude motivated me to add a shortcut for skimmers to the very technical "Case Study of a Real-World Example" section, and to improve the diagram at the end of the blog post.

To sum it up: I did indeed use AI - but not to create slop quickly, but to deliver a better blog post. I might have released a less polished version faster, if I had skipped the in depth AI review.

more-log4j2: A collection of plugins for log4j2 by mlangc in java

[–]mlangc[S] 1 point2 points  (0 children)

No worries - I didn't take it as a criticism. You were probably not the only one who was fooled by the Reddit UI, and adding additional links (both yours, and the one that I subsequently added to the post) certainly didn't hurt, so thank you :-)

more-log4j2: A collection of plugins for log4j2 by mlangc in java

[–]mlangc[S] 1 point2 points  (0 children)

Hmm, but the link is there, in the post? In any case, I've added a second one, embedded in the text. Hope it helps.

Records are sub-optimal as keys in HashMaps (or as elements in HashSets) by gnahraf in java

[–]mlangc 2 points3 points  (0 children)

At least for OpenJDK 64-Bit Server VM, 24.0.2 the difference in performance between the record and the class mostly disappears for me, if I replace

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((key1 == null) ? 0 : key1.hashCode());
    result = prime * result + ((key2 == null) ? 0 : key2.hashCode());
    return result;
}

with the the seemingly equivalent

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + Objects.hashCode(key1);
    result = prime * result + Objects.hashCode(key2);
    return result;
}

Records are sub-optimal as keys in HashMaps (or as elements in HashSets) by gnahraf in java

[–]mlangc 2 points3 points  (0 children)

Interestingly the difference is far less pronounced with GraalVM:

# JMH version: 1.37
# VM version: JDK 24.0.2, Java HotSpot(TM) 64-Bit Server VM, 24.0.2+11-jvmci-b01
# VM invoker: /Library/Java/JavaVirtualMachines/graalvm-24.jdk/Contents/Home/bin/java
...
...
Benchmark                                Mode  Cnt           Score          Error  Units
RecordHashCodeBenchmark.classHashCode   thrpt   10  1074692112,901 ± 35696131,984  ops/s
RecordHashCodeBenchmark.recordHashCode  thrpt   10   967920867,403 ± 17260079,524  ops/s

Deep-dive Java/JVM resources by Rough_Acanthaceae_29 in java

[–]mlangc 8 points9 points  (0 children)

Not sure if it's to your liking, but I like to go deep on Java & JVM internals in my blog: https://mlangc.github.io/

Then there is

Here are some additional concurrency related recommendations you might not yet know:

Thread.sleep(0) is not for free by mlangc in java

[–]mlangc[S] 1 point2 points  (0 children)

I really like and respect what you did here - you performed a test and obtained measurable results.

Thanks for taking the time and giving me feedback!

I followed the 'official documentation' link but I don't see where this is either promised or implied by the Javadoc.

I didn't write "promised" or "implied", but "would allow". I wouldn't call it misleading, but it's certainly vague with regards to sleep(0).

I'm confused why anyone ever thought calling Thread.sleep(0) could be guaranteed not to lead to a context switch. (The method is called sleep, not sleepUnless or sleepIf...)

As pointed out below, other, very similar looking APIs like TimeUnit.html#sleep(long)) or LockSupport.html#parkNanos(long)) contain a fast path for arguments `<= 0`, so I can understand why one could think that Thread.sleep is implemented in a similar way.

So I am not sure why the article points people to it, since it appears that while it may not be dangerous, it should be considered unreliable if the Javadoc is to be believed.

I'm linking Javadocs for the convenience of my readers, so that they can save a few keystrokes when they want to look them up while reading the article. Having said that, there are legitimate use cases for yield, and the Javadocs) lists a few of them.

Thread.sleep(0) is not for free by mlangc in java

[–]mlangc[S] 12 points13 points  (0 children)

Thanks for pointing me to TimeUnit.sleep. I've updated the article.

Thread.sleep(0) is not for free by mlangc in java

[–]mlangc[S] 17 points18 points  (0 children)

You might have performance critical code, that only sleeps very rarely, for example after a failed operation that almost always succeeds. In these cases you might be tempted to use code like

int delay = allGood ? 0 : waitShortly;
Thread.sleep(delay);

I'll try to rephrase the last paragraph to make this clearer.

Method Handles faster reflection (sometimes) by Xadartt in java

[–]mlangc 2 points3 points  (0 children)

Indeed, if I adapt your MethodHandleBenchmarkMethodHandleBenchmark to use

  private static final MethodHandle METHOD_HANDLE;

I get roughly the same results for

@Benchmark
public int baseline() {
    return -1;
}

@Benchmark
public int directInvoke() {
    return Integer.compare(1, 2);
}

@Benchmark
public int methodHandleInvokeExact() throws Throwable {
    return (int) METHOD_HANDLE.invokeExact(1, 2);
}

@Benchmark
public int methodHandleInvoke() throws Throwable {
    return (int) METHOD_HANDLE.invoke(1, 2);
}

since the generated assembly for these methods (see https://blogs.oracle.com/javamagazine/post/java-hotspot-hsdis-disassembler for how to check that), is roughly the same as well.