back
8 comments
Did you see the examples? Even with the blocking threaded style the Java versions gave me nightmares. And they didn't even bring up a cancelation example (not 100% sure on that). Cancelation and subsequent graceful shutdown is one of the hardest concurrency problems that (a) you are very likely to need in practice and (b) conveniently often are left out of example code. Heck it isn't even great in Go (too explicit imo) but at least they solved it.
Can you elaborate more?

All of the parts that throw `InterruptedException` are handling the thread interrupt cancelation mechanism.

There is also the example with the atomic boolean quit flag.

I'm not convinced that Go has any unique mechanisms here. Just like Java you can't forcefully kill a thread without that thread's cooperation.

Go provides a context package which allows in-flight cancellation of heavy operations spanning processes and even machines. https://pkg.go.dev/context
This is also part of how you could interrupt `time.Sleep`.

> In Go there is less noise, but also there no way to interrupt Go's time.Sleep.

The full piece would be something like:

    func sleepCtx(ctx context.Context, delay time.Duration) {
        select {
        case <-ctx.Done():
        case <-time.After(delay):
        }
    }

    func main() {
        fmt.Printf("%v\n", time.Now())
        ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
        defer cancel()
        sleepCtx(ctx, 1*time.Second)
        fmt.Printf("%v\n", time.Now())
    }
Runnable example on Go playground: https://go.dev/play/p/S5TY3CRmsYO
Yep this is a utility function in sure thousands have written, including myself. It's not in std which is weird but go had been a bit inconsistent since context was a big api change.

Fun fact: that func has a hidden bug, if one should be pedantic. Can you spot it?

> Fun fact: that func has a hidden bug, if one should be pedantic. Can you spot it?

Are you referring to the fact that the timer is still hanging around? Would this be the most-correct version?

    func sleepCtx(ctx context.Context, delay time.Duration) {
            t := time.NewTimer(delay)
            defer func() {
                    if !t.Stop() {
                            <-t.C
                    }
            }()
            select {
            case <-ctx.Done():
            case <-t.C:
            }
    }
[Weird, I remember already replying to this comment.]

Yes that's what I was thinking of :)

I'm a bit unsure what's the value in draining the channel (which is internal to this func), given that Go should garbage collect channel (and in this case perhaps even some escape analysis).

EDIT: Ah they aren't just closing the channel, but sending a time.Time. That makes sense, the timer wouldn't have anyone to send to if it's unbuffered.

Okay this is cool.

I think mechanically the plans for ScopeLocals can be a part of implementing a scheme like that. Either way - I need to read this thoroughly in the morning.

I deploy a single golang binary to well over 15k servers (many are only 100mbit) across many data centers and update it many times a day/week. Using xz, it compresses down to about 5mb.

As much as I love Java (I co-founded Apache Java), golang is a great fit for this usecase.

Okay, I'll bite. So why not distribute a most likely even smaller jar file?
The majority of these machines are PXE booted, with a super minimal ubuntu based OS distribution... which would mean distributing a JVM as well.

Total boot payload is about 160megs... which I need to do some more work on... I think I can get this down to about 120megs, but it hasn't been a priority yet since this is working well enough for now. About 54megs of that boot is just some third party drivers and right now, those are .gz encoded... need to switch to xz to bring it down.

Not the commenter:

I presume it would have to be because there is no guarantee that the JVM will be installed and configured exactly correctly on all of those machines a priori.

Jlink helps solve that problem if you have a properly modularized project, but that's hard to do if your dependencies aren't also properly modularized.

Isn’t that the case with libc and the like even in case of go? You can’t just expect any program to run correctly on an environment you don’t control.
Said servers would also need Java installed and updated.

The Go version needs that executable, nothing else.

Doesn't GraalVM as mentioned in the root comment solve that problem? IIRC Graal can compile a JVM + Java application to a single binary, but I could be mistaken.
That's the issue with the size of the binaries produced... you're effectively bundling a JVM.

In the same way that golang is bundling a GC implementation... except it is orders of magnitude smaller than a JVM.

I find it hard to be convinced to use GO at all. If i wanted efficiency, i'd bite and go with something close to the metal. If i wanted something that can build huge applications and has a rich framework ecosystem - i'd go with Java. GO seems like a middle solution for some exceptional cases, which i haven't deal with.

It is a fresh language and i quite enjoyed exploring it, but i just can't see the use of it. (also, the multiple return value functions are great, but if only there was a way to use a particular value directly from the function without having to assign them to variables)

Graalvm takes some special effort to get things building natively and the binaries are far larger, so I'm going to go with "yeah".

And I'm hardly a fan of Go.

Why not ? Go binaries don't require a seperate vm/runtime. Theyre faster in some cases and Go has overall simpler to read code than Java.

There is room enough for both languages.

The modern Java packaging also includes the runtime itself, there is not even a JRE anymore. And readability is somewhat subjective, Java is a really simple language, all the writer does is just method calls into various existing libraries. Since those didn’t get a chance to evolve yet with the upcoming virtual threads they are a bit longer at times.

So all what left is opinionated in-language support vs library support, and it is not clear cut which is the winner. Java seems to allow finer control over the primitives of concurrency, so a library or another JVM language could build up a much better abstraction over the mechanism.

I think the differences would drop off significantly if one were to write Java while keeping the Go guidelines and proverbs in mind; that is, reduce or eliminate the amount of 3rd party libraries (you don't actually need a dependency injection / wiring framework for most applications, just instantiate your objects with dependencies by hand in your main method), reduce the amount of abstraction layers, don't try to be clever, stick with regular loops instead of Streams, etc etc.
I agree, though do keep in mind that a complex framework like Spring doesn’t play in the same league as a typical go microservice in terms of what kind of application is getting written in them.

But I really hope that the new, record-based, minimal boilerplate java style gets hyped up more and more, we really should prefer compile time metaprogramming instead of reflection-based solutions. (E.g. mapstruct is really cool!)

Golang still has an advantage over Java + Loom + Graalvm, JVM as a better GC but Golang using more inline/value types puts less pressure on it and diminishes memory usage. So you need to wait also for Java project Valhalla to match Golang profile.
Hypothetically there is a version of "static Java" that could start to encroach upon Go's domain. This may or may not come out of Project Leyden, but I think for the reason the other commenter gave GraalVM isn't exactly it.

Still closer though, and GraalVM native images are pretty cool.

https://mail.openjdk.java.net/pipermail/discuss/2020-April/0...

Only when one is forced to deal with Docker or k8s ecosystem stuff written in Go.

Or as a type safe C for userspace.