goland 互斥锁 读写锁

互斥锁

每个资源都对应于一个可称为 "互斥锁" 的标记,这个标记用来保证在任意时刻,只能有一个协程(线程)访问该资源。其它的协程只能等待。

互斥锁是传统并发编程对共享资源进行访问控制的主要手段,它由标准库sync中的Mutex结构体类型表示。sync.Mutex类型只有两个公开的指针方法,Lock和Unlock。Lock锁定当前的共享资源,Unlock进行解锁。

在使用互斥锁时,一定要注意:对资源操作完成后,一定要解锁,否则会出现流程执行异常,死锁等问题。通常借助defer。锁定后,立即使用defer语句保证互斥锁及时解锁。

var mt sync.Mutex
mt.Lock()    // 加锁
...         // 共享资源
mt.Unlock()  // 解锁
package main

import (
	"sync"
	"time"
)
import "fmt"

var mt sync.Mutex
func test(str string)  {
    mt.Lock()
	for _, s := range str{
		//fmt.Println(s)  // 打印的是字节
		fmt.Printf("%c",s)
		time.Sleep(time.Millisecond * 100)
	}
    mt.Unlock()
}

func foo(str string)  {
	test(str)
}

func fun(str string)  {
	test(str)
}

func main() {
	go foo("hello")
	go fun("world")

	for {
		;
	}
}

读写锁

互斥锁的本质是当一个goroutine访问的时候,其他goroutine都不能访问。这样在资源同步,避免竞争的同时也降低了程序的并发性能。程序由原来的并行执行变成了串行执行

当我们对一个不会变化的数据只做“读”操作的话,是不存在资源竞争的问题的,因为读不会对数据进行更改,问题不是出在“读”上,主要是修改,也就是“写”。修改的数据要同步,这样其他goroutine才可以感知到。所以真正的互斥应该是读取和修改、修改和修改之间,读和读是没有互斥操作的必要的。

因此,衍生出另外一种锁,叫做读写锁

读写锁可以让多个读操作并发,同时读取,但是对于写操作是完全互斥的。也就是说,当一个goroutine进行写操作的时候,其他goroutine既不能进行读操作,也不能进行写操作。

GO中的读写锁由结构体类型sync.RWMutex表示。此类型的方法集合中包含两对方法:

一组是对写操作的锁定和解锁,简称“写锁定”和“写解锁”:

func (*RWMutex)Lock()

func (*RWMutex)Unlock()

另一组表示对读操作的锁定和解锁,简称为“读锁定”与“读解锁”:

func (*RWMutex)RLock()

func (*RWMutex)RUlock()

案例:

var rwMut sync.RWMutex
var value  int

func readGo(i int)  {
	for{
		rwMut.RLock()  // 加读锁
		num:= value
		fmt.Printf("%d th , read %d \n", i, num)
		rwMut.RUnlock()  // 解读锁
	}
}
func writeGo(i int, qu chan <- int)  {
	for q := 1;q < 500; q++{
		if q == 10{
			qu <- 1
		}
		num := rand.Intn(1000)
		rwMut.Lock()  // 加写锁
		value = num
		fmt.Printf("%d th , write %d \n", i, num)

		time.Sleep(time.Millisecond * 100)
		rwMut.Unlock()  // 解写锁
	}
}


func main() {
	quit := make(chan int)
	rand.Seed(time.Now().UnixNano())
	for i:=1;i < 5 ;i++  {
		go readGo(i+1)
	}

	for i:=1;i < 5 ;i++  {
		go writeGo(i+1, quit)
	}
	<- quit
}

>>> :
3 th , write 466  // 写独占
4 th , read 466   // 读共享
2 th , read 466
3 th , read 466
5 th , read 466
4 th , write 646
4 th , read 646
3 th , read 646
2 th , read 646
5 th , read 646
5 th , write 319
....

上一篇:ReentrantLock 及 AQS 实现原理


下一篇:Java ReentrantLock 可重入锁