> The prime difference is in the stance: "Is concurrency a library or is it a built-in feature?". In Java, it is the former, whereas in Go, it is the latter.
Fully agree. But in case of Go, the language feature is non performant.
cd $GOROOT/src
grep -r Lock *
You are strongly motivating me to write Go channel semantics over Java's (nio) Selectors and j.u.c to demonstrate a point... (Don't. I've other things to do.)
Go with goroutines and channels is a nice way to handle concurrency.
At least it was the one that was easiest for me to wrap my head around and actually improved the performance of my code without weird race conditions or bugs.
There’s a difference between sync and async code in Go, which is why you have all the normal threading primitives like mutexes, semaphores and blocking queues/channels.
The point is that functions themselves don’t come in sync or async flavors. Just like in Java.
> Queues ("channels") are a good way to limit complexity of threaded code by treating each process as an agent. Besides some syntactical sugar Go doesn't really support this better than most other languages with threading, like Java.
The magic of channels comes with select{}. Considering them to be only threadsafe queues is really missing out.
Supporting select{} in other languages is possible, but difficult and rare.
It goes deeper than that. The prime difference is in the stance: "Is concurrency a library or is it a built-in feature?". In Java, it is the former, whereas in Go, it is the latter.
In my experience, having concurrency primitives in the language tend to integrate better than having them in a library - for the simple fact it saves some space when writing and reading programs.
As for the choice of `java.util.concurrent` over goroutines it again depends on your stance. j.u.c provides you with some excellent high-level primitives on which to build concurrency. But these primitives are easy to build in Go with its concurrency primitives. Some, like `BlockingQueue<E>` or `Future<V>` are directly isomorphic to a channel or a simple goroutine/channel invocation respectively. It is a matter of opinion whether or not one should actually do these kinds of implementations themselves.
A more important difference, however, is that Java operates on Threads as the "smallest" primitive, whereas a goroutine is a light-weight "thread". That is, you can easily have tens of thousands of goroutines running, but you do probably not want the same amount of threads in a Java system. For that, you need stuff like kilim.
In my experience, I am a professional Erlang programmer, the ability to run extremely concurrent threads/processes is a game changer for much of the code you will be writing.
I think one huge difference is that a channel in Go is a stream of data, in which each element can cause the consuming code to wait for new responses. In your example, the future can only return one bit of data.
Channels and goroutines advertised as the poster child of Go language features for simplifying concurrency since the beginning. When I tried to use them I was surprised how difficult actually it is to use them correctly, so many subtleties to deal with.
Yes they are addictive, but I suspect beginners really are not using them correctly.
> I think one huge difference is that a channel in Go is a stream of data, in which each element can cause the consuming code to wait for new responses. In your example, the future can only return one bit of data.
In java you also can do something like:
List<Future<Long>> futures = new ArrayList<>();
for(int i = 0; i < n; i ++) futures.add(some parallel execution logic);
Nah, totally different than channels. And Goroutines are proper managed threads with their own stacks.
As an aside pipelines are terriblely unergonomic. The public APIs are not fully developed and something simple, like IDK creating an actual processing pipeline, is funky as all hell. Creating a pipe wrapper feels dirty.
The buffer management is cool though and sequence seems like it should have just been made a first class slice type..
This is a thing I have to continually remind newer engs that are using Go. Goroutines have exactly the same memory observability / race conditions / need for locking/etc as any threaded language. Every race bug that exists in e.g. Java also exists in Go. The only real difference for most code is that it pushes you very hard to use channels directly, as they're the only type-safe option.
There are some neat tricks lower down, e.g. where you don't have to explicitly reserve a thread for most syscalls (but there are caveats there too, which is why runtime.LockOSThread() exists), which are legitimately nice conveniences. But not a whole lot else.
Are you kidding me? This post is so full of arrogance and brazen oversimplifications of both Java and Go, there is so little of actual value to even bother discussing.
It's almost like you don't realize that Java came out a decade before Go, or that you don't understand the power of Goroutines or the power of writing your own low-level concurrent code.
I'd hate for you to find out that java's concurrency primitives are probably implemented using the same constructs as goroutines and/or are available to devs in the sync package. (Feel free to write "thread"-safe data structures. Hell, if you'd wanted to complain about the lack of generics in that case, I'd have even joined you.)
Mmmm I'm puzzled. I don't think we are consulting the same community... Goroutines are everywhere in all the main go projects. Goroutines and channels are one of the main reasons Go exists.
Go still has the feature of lightweight threads (goroutines) by default and great communication and synchronization primitives (channels, select) between them. For Java you still have to choose either real threads or from one of dozens of not-necessarily-compatible async IO frameworks (Netty, Grizzly, ...). How much that really matters depends on the type of your application. There's also emulation of Gos concurrency through Quasar - but again it's not as first class as Gos features.
I really like how Go does message passing, and other than channels being first class types, I don't think that's really the "killer feature" of Go. The killer feature is that goroutines are green threads, scheduled in M:N fashion on to OS threads. This encourages concurrent programming because spinning up a goroutine is comparatively cheap to spinning up an OS thread. It's difficult to do this kind of programming in most other languages (sans Erlang, Rust and Haskell).
Joe Armstrong made this argument years ago. He compared the limited ability to start processes in most languages as being similar to limiting how many objects you could create in your program.
If you want to see examples of concurrent programming in Go, go straight to the source: http://golang.org --- The tour is good, there are some codewalks, talks, articles, etc.
> I think the misunderstanding here is that goroutines are not classical threads (which would share many problems with `goto`) - they exist somewhere between coroutines and actors (because of channels+select).
I don't think that comparison makes sense. Channels and select are not a property of goroutines themselves, nor is using those the only way to communicate with other goroutines. The actual functionality of a goroutine is very similar to a thread, even if the broader language/conventions push it towards a coroutine/actor.
> Goroutines share more in common with method calls (which are a form of branching) and even with single-threaded scenarios, methods can do surprising things - especially if you have shared global state.
But they can't run in parallel, or data race. Goroutines, like classical threads, can have data races.
> Additionally, if you control how data is shared (such as message passing - channels) you shouldn't have to be concerned about what the other thread is doing - so long as it reacts to messages that you send to it.
This is also true of threads. Message passing is a layer on top of, and agnostic to, some concurrency mechanism. Go might make it slightly syntactically simpler than languages that use true OS threads (much simpler than C's pthread_spawn, but not particularly different to Rust's thread::spawn(some_closure)), but that's a syntax layer.
java.util.concurrent offers tasks and queues.
As for the rest, they are not unique to Go, better languages offer similar features.
reply