what is the golang equivalent of a Java synchronized() block?
Asked Answered
I

3

24

Java provides a very convenient idiom for synchronizing critical portions of code:

synchronized(someObject) {
    // do something really important all by myself with nobody bothering me
}

Or

public synchronized void doSomething() {
    // ...
}

What is the go equivalent?

(A quick search reveals: golang.org/pkg/sync/ - which seems (maybe I'm wrong) a bit too low level for general use.)

(Example of why I care about this: I need to send a message to multiple listeners via channels. Channels provide a good conduit for the data without having to synchronize anything, but when channels are added or removed I need to modify the list of channels, which might happen at any time must be able to deal with concurrency.)

Illustrious answered 18/9, 2013 at 19:20 Comment(0)
C
23

sync.Mutex is a mutual exclusion lock, it can provide a similar functionality to the synchronized java key-word (except that locks in java provide reentrant mutual exclusion) :

synchronized(someObject) {
    //   
}

Is equivalent to :

var l sync.Mutex

l.Lock()
//
l.Unlock()
Charismatic answered 18/9, 2013 at 19:39 Comment(5)
The semantics aren't the same. Java synchronized blocks provide re-entrant locks.Centrosome
Yeah, you are right @Anonymous ,but Go don't have java-like class inheritance, so I believe that there is no need for a reentrant locks in Go.Charismatic
play.golang.org/p/jfeBxkve65 would work in Java, but deadlocks in Go. Inheritance plays no part.Centrosome
Some discussion here: https://mcmap.net/q/398284/-recursive-locking-in-goCentrosome
For my application I do not need reentrance (and IMO this would be the most common scenario), so this would work.Illustrious
M
16

to extend off tarrsalah's answer.

You can add sync.Mutex to your object, allowing them to be directly locked and unlocked.

type MyObject struct{
    Number int
    sync.Mutex
}

func (m *MyObject)Increment(){
    m.Lock()
    defer m.Unlock()
    m.Number++
}

Defer'd commands will run at the end of the function, this way you know it gets both locked and unlocked, in bigger functions.

Mesenchyme answered 1/1, 2016 at 9:44 Comment(1)
This answer is better because it mentions defer. Calling unlock() manually is prone to deadlocksBarrelchested
C
9

A different solution to using a mutex is to use a channel to communicate listener changes.

A full example in this style looks like this. The interesting code is in FanOuter.

package main

import (
    "fmt"
    "time"
)

type Message int

type ListenerUpdate struct {
    Add      bool
    Listener chan Message
}

// FanOuter maintains listeners, and forwards messages from msgc
// to each of them. Updates on listc can add or remove a listener.
func FanOuter(msgc chan Message, listc chan ListenerUpdate) {
    lstrs := map[chan Message]struct{}{}
    for {
        select {
        case m := <-msgc:
            for k := range lstrs {
                k <- m
            }
        case lup := <-listc:
            if lup.Add {
                lstrs[lup.Listener] = struct{}{}
            } else {
                delete(lstrs, lup.Listener)
            }
        }
    }
}

func main() {
    msgc := make(chan Message)
    listc := make(chan ListenerUpdate)
    go FanOuter(msgc, listc)
    // Slowly add listeners, then slowly remove them.
    go func() {
        chans := make([]chan Message, 10)
        // Adding listeners.
        for i := range chans {
            chans[i] = make(chan Message)
            // A listener prints its id and any messages received.
            go func(i int, c chan Message) {
                for {
                    m := <-c
                    fmt.Printf("%d received %d\n", i, m)
                }
            }(i, chans[i])
            listc <- ListenerUpdate{true, chans[i]}
            time.Sleep(300 * time.Millisecond)
        }
        // Removing listeners.
        for i := range chans {
            listc <- ListenerUpdate{false, chans[i]}
            time.Sleep(300 * time.Millisecond)
        }
    }()
    // Every second send a message to the fanouter.
    for i := 0; i < 10; i++ {
        fmt.Println("About to send ", i)
        msgc <- Message(i)
        time.Sleep(1 * time.Second)
    }
}
Centrosome answered 19/9, 2013 at 14:11 Comment(1)
This is a really good answer as it don't simply translate a lock from java to go, it uses go to resolve the initial problem you have with concurrency. It also clearly answers the problem stated .. +1Caryl

© 2022 - 2024 — McMap. All rights reserved.