SoFunction
Updated on 2025-03-05

The specific use of optimistic locks and pessimistic locks in Go language

Three steps to change a value

  • Take out the value you want to modify from somewhere
  • Modify the retrieved value to the expected value
  • Save the modified value to the original place

question

If another process (process or thread) performs the same operation (value, modification) on the same value when both processes need to do step 3, there must be one process for nothing.

Pessimistic lock

The pessimistic lock always believes that concurrency problems will occur and belong to the conservative.
If you want to modify a value, immediately add a lock to the value to indicate that the value is being modified and no one can modify it; then start three steps, and after the three steps process is over, the lock will be unlocked.

When there are other processes that want to modify the same value, when you see the lock, you don’t go through three steps, but choose to wait; when the lock is unlocked, you also add a lock to the value, and then start three steps. After the three steps are completed, the lock will be unlocked.

Optimistic lock

Optimistic Lock always believes that concurrency problems will not occur and belong to the Optimistic School.

When modifying the data, do not add locks. Steps 1 and 2 are normally carried out. When performing step 3, confirm whether the value has been modified. If it has been modified, give up the modification and go again to steps 1, 2, and 3 (or give up modifying the value).

Optimistic locks and pessimistic locks in Go language

sync/atomic

Go language has an atomic package that can complete concurrent and safe value replacement operations without forming critical areas and creating mutexes. This package applies the principle of optimistic locking.
However, this package only supports some basic operations of int32/int64/uint32/uint64/uintptr, such as addition and deduction, exchange, loading, storage, etc.

sync

The sync package in Go language provides various locks. If this package is used, it will basically be in the working mode of pessimistic locks.

Go code example

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

var (
	x  int64
	mu 
	wg 
)

// Normal functions, concurrency is not safefunc Add() {
	x++
	()
}

// Mutex lock, concurrent security, performance lower than atomic operationfunc muAdd() {
	()
	x++
	()
	()
}

// Atomic operation, concurrency security, performance is higher than mutex locks, and is only used for some basic data types in gofunc AmAdd() {
	atomic.AddInt64(&x, 1)
	()
}

func main() {
	// Atomic operation atomic package	// Locking operation involves kernel-state context switching, which is time-consuming and expensive	// We can also use atomic operations to ensure concurrency security for basic data types	// Because atomic operations are methods provided by Go language, we can complete them in the user state, so the performance is better than locking operations	// Atomic operation of the go language is completed by the built-in library, sync/atomic
	start := ()
	for i := 0; i < 10000; i++ {
		(1)
		go Add() // The normal version of the Add function is not concurrency-safe		// go muAdd() // The locked version of Add function is concurrently safe, but the locking performance is expensive		// go AmAdd() // Atomic operation version Add function is concurrency-safe and has better performance than locked version	}

	end := ()
	()
	(x)
	((start))

}

Reference Blog 1

Reference Blog 2

This is the article about the specific use of optimistic locks and pessimistic locks in Go language. For more related Go languages, please search for my previous articles or continue to browse the related articles below. I hope everyone will support me in the future!