$ \def\Vec#1{\mathbf{#1}} \def\vt#1{\Vec{v}_{#1}(t)} \def\v#1{\Vec{v}_{#1}} \def\vx#1{\Vec{x}_{#1}} \def\av{\bar{\Vec{v}}} \def\vdel{\Vec{\Delta}} $

Harald Kirsch

about this blog
2024-06-04

Java Wishlist: Thread Safety

In the 1990s I felt proud to know Plauger's The Standard C Library nearly by heart, as well as many of the tools C has ready for you to really screw up. Many of the latter gather around fails to do the right thing with bytes provided by memory allocation, malloc().

It was a revelation to start coding in Java. Suddenly I could concentrate on the programming problem to solve, rather than remembering shit like you shall not use scanf("%s", buf) because it may write over the end of buf.

Then came multi-core computers and multi-threaded Java progams. And over time I realized that a developer's responsibility around Java's thread communication is as shitty as it is in the area of C's memory handling. You have to remember stupid things that have nothing to do with the problem you want to solve.

Wooooowoaaa, wait, Harald. There are those great tools like synchronization and queues and concurrent maps and thread local variables and all that. Ehem, so what. Just consider a class like

  public class Stuff {
    public long value;
  }

If an object of this class gets into the hands of more than one thread at a time, both changing the public value, the result may not just be last-writer-wins, but could be a fucked up mixture of both values, because ...? Well, why? You, as the developer, know why? And this is the problem.

If you develop in Java long enough, you may not even notice any more that we have a problem here. Like you don't touch a hot oven, like you don't walk over a red light on a busy street. Yet, despite knowing, people burn themselves and get run over.

My Wish

The developer should not need to know or care about whether a class being designed may be used concurrently or not.

I am not asking the impossible of preventing logical problems inherent to multi-threading. If the value of an object of Stuff is set to -3 and 18 "at the same time", then of course one or the other must survive, we can't have both. But the value should be one or the other, not some garbage because the two 4 byte parts making up the long are changed non-atomicly.

But, Harald, we already have this: just declare the whole class as synchronized and introduce setter and getter:

  public synchronized class Stuff {
    private long value;
    void set(long value) { this.value = value; }
    long get() { return value; }
  }

Oooh, we have this? But nobody uses it regularly. <sarcasm>Is there something wrong with it?</sarcasm>

Half Baked Solution

I am not a language designer, so this is just some odd idea of mine which may be totally impossible to implement. The underlying problem arises from more than one thread at a time messing with a bunch of bytes which must be changed in combination atomically.

Peppering the code with synchronized keywords is one way to enforce it.

How about classes such that none of their instances, extending recursively to their fields, are allowed to be seen by more than one thread. And add a way of communicating such object's values between threads explicitly.

One such type of classes are recursively immutable classes. Which Java does not provide in a way where the compiler enforces it. You have to manually take care of it.

Another way could the actor model while also making sure that only object's values and not references to objects are communicated between the actors, except if the compiler could prove it is OK.

My main point is: the compiler should enforce things. Because, quite trivially, every mistake the compiler notices is one that I am never again going to make.