Hacker News new | past | comments | ask | show | jobs | submit login

Java has a large set of higher level abstractions for concurrency. You don't have to use low level locks but you can. (And that's just Java, there's also Scala, clojure ...)



I'm pretty firmly in the "shared memory parallelism is a Good Thing" camp, but the counter argument to your point is that having a larger set of concurrency abstractions is a Bad Thing in that any particular piece of code has to consider all of the different permutations. In a shared-nothing world, there's a lot less to worry about (except occasionally performance).


The world writeable cross thread also has implications on how your GC algorithm is designed.

Erlang for instance scopes gc pools per process so short lived processes just drop the pool. Also GC of one worker doesn't stop any others. Can't remember if it even needs to be generational because the heaps are already sliced by process. It's the closest thing to heap arenas I've seen in a VM based language.

Or take Lua which is single threaded and doesn't require VM safepoints since everything is done via cooperative coroutines.

Java needs to assume worst case and as such has to be conservative in some of it's approaches.


As written here: https://github.com/l3nz/SlicedBread - "the over 400 rich pages of "Java concurrency in practice" show how hard it is to write and debug a good-mannered multithreaded application in standard Java."


... what are they?


https://docs.oracle.com/javase/8/docs/api/java/util/concurre...

And given that you didn't know that, you really need to study them.

java.util.concurrent is one of the greatest gems of software ever written.


So I describe the shortcomings of CompletableFutures, and you point out that there's a java.util.concurrent package?

Is this method one of its gems? A cancel() which doesn't cancel? https://docs.oracle.com/javase/8/docs/api/java/util/concurre...

> Synchronized primitives don't compose. You can safely `synchronized get(...)` and safely `synchronized put(...)`. But their composition put(get(...)+1) isn't synchronized.

So is there anything in java.util.concurrent that does compose? put(get()) has exactly the same problem up here at the 'high level' (of CountdownLatches and Semaphores) as it does at the 'low level' (of synchronized methods.)


Java does not allow one thread to kill another due to its shared memory concurrency model. It actually used to, but this feature was removed because it caused so many deadlocks. The reason is that killing threads won't always release monitors and locks. Lack of the feature is intentional.

You can get a lot better nonblocking support with third party libraries. Like RxJS in javascript, RxJava is almost a requirement when doing non-blocking code.

You are right that true green threads would allow thread cancellation one day. Right now, Java can't because it relies on OS threads which aren't safe to cancel. Userspace threads don't have that problem


> Java does not allow one thread to kill another due to its shared memory concurrency model. The reason is that killing threads won't always release monitors and locks.

I can't follow the reasoning here. To refute it, you'd only need to show a language with shared memory and futures with cancel(), right? Aside from that, a second shortcoming isn't a good excuse for the first.

> You can get a lot better nonblocking support with third party libraries.

I don't doubt this. Another example is vavr.io, which gives much better Lists/Optionals/Streams than the standard Java 8 ones. And Joda-Time before that.

> RxJava is almost a requirement when doing non-blocking code.

Why can't CompletableFutures do what RxJava did?

> it relies on OS threads which aren't safe to cancel. Userspace threads don't have that problem

This bit was the most confusing to me. In my shitty understanding of the model, Java Threads are based on OS threads, which is why they're relatively heavy. CFs on the other hand are are lighter and managed by the Java runtime. Why is it then that I can cancel Threads, but I cannot cancel CFs. Your explanation would seem to justify the opposite.


Maybe I muddled my words on this. Java supports "requesting" thread cancellation, but its not safe to forcibly kill OS threads in any language I know of. For the same reason you can't in Java, finalizers won't run. This includes C and C# among others that use OS threads. https://stackoverflow.com/questions/13285375/how-to-kill-a-r... https://stackoverflow.com/questions/1327102/how-to-kill-a-th...

Notably C lets you do it with the big warning that it can crash everything. C# made the same decision as Java and doesn't allow it.

Java and pretty much all OS thread-using languages DO allow you to end threads by nicely asking them to stop. This is not the same as forcing them to. In all cases, if a thread is stuck in an infinite loop or a blocking call, it probably won't cancel if you ask it to. It depends on whether the thread is written to handle cancellation properly.

This is also a limitation of some languages with fibers, including Erlang. You can't force a thread thats stuck in certain states to stop running in Erlang even though it uses fibers. Some languages with fibers do allow it though.


> Maybe I muddled my words on this.

Not at all. Thanks for your patience.

You've laid out really good arguments as to why Java Threads cannot be cancelled, and suggested that userspace threads -- which I (perhaps mistakenly) interpreted as CompletableFutures -- should be able to be cancelled.

But the thing is, I can cancel (request) Java Threads. And I cannot cancel CompletableFutures.

From my original comment:

> But CFs lack the functionality of Threads. A CF can't decide to sleep for a while, nor can it be cancelled.


What conditions do you need to get an Erlang process that you can't kill? Brutal kill, or load the executing module twice always worked for me (except for a couple deadlock bugs my company added to our locally patched BEAM)


What do you mean the cancel method doesn't cancel ? It does cancel with a CancellationException stored in the future and an optional interrupt send to the thread running the blocking operation.


> optional interrupt send to the thread running the blocking operation

I'm not 100% sure on what you mean by an optional interrupt, but I'm guessing it's the boolean flag on the cancel(); Let's look!

    public boolean cancel(boolean mayInterruptIfRunning) {
        boolean cancelled = this.result == null && this.internalComplete(new CompletableFuture.AltResult(new CancellationException()));
        this.postComplete();
        return cancelled || this.isCancelled();
    }
Doesn't look like it's used.


You are right. Even the javadoc says that `CompletableFuture` doesn't support this. Bah! will force me read the fine print next time.

mayInterruptIfRunning - this value has no effect in this implementation because interrupts are not used to control processing.


> What do you mean the cancel method doesn't cancel?

Try it.

    @Test
    public void cannotCancelARunningFuture() {

        // Future that just sleeps
        CompletableFuture<Void> sleeping =
                CompletableFuture.supplyAsync(() -> {
                    int i = 0;
                    while (true) {
                        System.out.println("zzzz: " + i++);
                        threadSleep(300);
                    }
                });

        // Make sure it started
        threadSleep(1000);

        sleeping.cancel(true);
        System.out.println("Sleep was cancelled");

        threadSleep(1000);
        System.out.println("Haha no it wasn't");
    }
===============================

  zzzz: 0
  zzzz: 1
  zzzz: 2
  zzzz: 3
  Sleep was cancelled
  zzzz: 4
  zzzz: 5
  zzzz: 6
  Haha no it wasn't




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: