CodeNebula
Go⚡Concurrencyintermediate

sync.Mutex and sync.RWMutex

Go · Concurrency · Run with go run

Explanation

When goroutines share mutable state (instead of communicating via channels), a mutex serializes access. `sync.Mutex` provides exclusive locking (`Lock`/`Unlock`). `sync.RWMutex` allows multiple concurrent readers OR one exclusive writer — use it when reads vastly outnumber writes.

Real-World Scenario

A concurrent cache stores results of expensive API calls. Multiple goroutines read from the cache simultaneously (RLock), but when a cache miss triggers a fill, one goroutine holds the write lock (Lock) while others wait — preventing duplicate fetches and data races.

Quick Snippet

snippet.go
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

// RWMutex for read-heavy cache
type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) (string, bool) {
    c.mu.RLock()         // multiple readers allowed simultaneously
    defer c.mu.RUnlock()
    v, ok := c.data[key]
    return v, ok
}

Full Example

mutex.go
// Compile: go run mutex.go
// Output:
//   final count: 1000
package main

import (
	"fmt"
	"sync"
)

type SafeCounter struct {
	mu    sync.Mutex
	count int
}

func (c *SafeCounter) Inc() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.count++
}

func (c *SafeCounter) Value() int {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.count
}

func main() {
	c := &SafeCounter{}

	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			c.Inc()
		}()
	}
	wg.Wait()

	fmt.Println("final count:", c.Value())
}
Run command
go run mutex.go