Use the sync/atomic package when you need lock-free, thread-safe updates to simple variables like integers, pointers, or booleans, avoiding the overhead of sync.Mutex for high-frequency counters or flags. These functions guarantee that read-modify-write sequences happen indivisibly, preventing race conditions without blocking goroutines.
For standard integer operations, use AddInt64, SwapInt64, or CompareAndSwapInt64 (CAS). CAS is particularly powerful for implementing complex lock-free algorithms where you need to update a value only if it hasn't changed since you last read it.
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64
// Increment safely without a mutex
atomic.AddInt64(&counter, 1)
// Swap the value and get the old one (useful for state machines)
oldVal := atomic.SwapInt64(&counter, 100)
fmt.Printf("Old: %d, New: %d\n", oldVal, atomic.LoadInt64(&counter))
// Compare-And-Swap: only update if current value is 100
success := atomic.CompareAndSwapInt64(&counter, 100, 200)
fmt.Printf("CAS Success: %v, Value: %d\n", success, atomic.LoadInt64(&counter))
}
For pointers, use LoadPointer and StorePointer to safely pass references between goroutines, or SwapPointer to atomically replace them. Be careful with 64-bit alignment on 32-bit systems; ensure the target variable is aligned to 64 bits (usually by placing it as the first field in a struct) or use atomic.LoadInt64/StoreInt64 on a *int64 that is properly aligned.
package main
import (
"fmt"
"sync/atomic"
)
type Data struct {
value int
}
func main() {
var ptr *Data
initial := &Data{value: 1}
// Store pointer safely
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&ptr)), (*unsafe.Pointer)(unsafe.Pointer(&initial)))
// Load pointer safely
loaded := (*Data)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&ptr))))
fmt.Printf("Loaded value: %d\n", loaded.value)
}
Avoid using atomic for complex data structures or multi-step transactions; if you need to update multiple fields atomically, use a sync.Mutex or sync.RWMutex instead. Atomic operations are strictly for single-word operations. Also, remember that while atomic prevents data races, it does not enforce memory ordering beyond what the specific function guarantees, so use atomic.Load/Store carefully when coordinating complex state changes across goroutines.