可重入锁的学习

java和Go在可重入锁上的对比

面试提到有关go是如何实现可重入锁的,都不太记得go有这个,记录下

  1. 可重入锁的概念:指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

  2. java的可重入锁:Java直接展示结果,是可以运行的,类似的代码结构在golang中会出现 竞争异常(java的synchronized也是可重入结构)
    可重入锁的学习_第1张图片

  3. go的锁是否是可重入的

    type Reentrant struct {
           
    	sync.Mutex
    
    }
    
    func (receiver *Reentrant) methodA()  {
           
    	receiver.Lock()
    	fmt.Println("method A is running")
    	receiver.methodB()
    
    	receiver.Unlock()
    }
    
    func (receiver Reentrant) methodB()  {
           
    	receiver.Lock()
    	fmt.Println("method B is running")
    	receiver.Unlock()
    }
    
    func TestReentrant(t *testing.T) {
           
    	a := new(Reentrant)
    	a.methodA()
    }
    

    可重入锁的学习_第2张图片

  4. go实习一个简单的可重入锁
    关于可重入锁的原理,需要存储的信息包括锁定状态(需要注意状态设置的原子性),持有锁的线程,以及重入的次数,其中针对go来说获取线程编号存在问题

    	import (
    	"fmt"
    	"runtime"
    	"strconv"
    	"strings"
    	"sync"
    	"sync/atomic"
    )
    
    type MyReentrantLock struct {
           
    	lock sync.Mutex
    	id int
    	counter int32
    }
    
    func (l *MyReentrantLock) Lock()  {
           
    
    	// 第一次锁
    	if atomic.CompareAndSwapInt32(&(l.counter), 0, 1){
           
    		l.lock.Lock()
    		l.id = GoID()
    	}else {
           
    		if GoID() == l.id{
           
    			atomic.AddInt32(&(l.counter), 1)
    		}else {
           
    			// 这里就是阻塞goroutine了
    			l.lock.Lock()
    		}
    	}
    
    
    
    
    
    }
    
    func (l *MyReentrantLock) Unlock()  {
           
    	// 当counter归零是真正释放锁
    	v := atomic.LoadInt32(&(l.counter))
    	if v == 0{
           
    		panic("this mutex is not locked")
    	}
    	if l.id == GoID(){
           
    		v := atomic.AddInt32(&(l.counter), -1)
    		if v == 0 {
           
    			l.lock.Unlock()
    		}
    	}
    }
    
    func GoID() int {
           
    	var buf [64]byte
    	n := runtime.Stack(buf[:], false)
    	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
    	id, err := strconv.Atoi(idField)
    	if err != nil {
           
    		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
    	}
    	return id
    }
    

参考链接

  1. 对可重入锁和不可重入锁的理解,他们的区别及实现原理解析
  2. 如何获取goroutine id

你可能感兴趣的:(golang,多线程)