可重入锁设计

go 实现可重入锁

实际上,Go 语言标准库中的 sync.Mutex 是不可重入的。但是,我们可以基于 sync.Mutex 实现一个可重入锁(ReentrantLock)。下面是一个简单的可重入锁的实现示例:


Go

1package main
2
3import (
4	"sync"
5)
6
7type ReentrantMutex struct {
8	mutex     sync.Mutex
9	holder    string
10	recursion int
11}
12
13func (r *ReentrantMutex) Lock() {
14	goroutineID := getGoroutineID() // 假设这是获取当前 goroutine ID 的函数
15
16	r.mutex.Lock()
17	if r.holder == goroutineID {
18		r.recursion++
19	} else {
20		r.holder = goroutineID
21		r.recursion = 1
22	}
23}
24
25func (r *ReentrantMutex) Unlock() {
26	goroutineID := getGoroutineID()
27
28	r.mutex.Lock()
29	defer r.mutex.Unlock()
30
31	if r.holder != goroutineID {
32		panic("attempted unlock from a goroutine that does not hold the lock")
33	}
34
35	r.recursion--
36	if r.recursion == 0 {
37		r.holder = ""
38	}
39}
40
41// 示例性的 getGoroutineID 函数,实际 Go 语言中没有直接提供获取 Goroutine ID 的方法
42func getGoroutineID() string {
43	// 这里仅作为示例,真实环境中需要自己实现或使用第三方库来获取 goroutine ID
44	return "goroutineID" // 真实情况会更复杂
45}
46
47func main() {
48	reentrantLock := &ReentrantMutex{}
49
50	go func() {
51		reentrantLock.Lock()
52		fmt.Println("Goroutine A acquired the lock.")
53		reentrantLock.Lock()
54		fmt.Println("Goroutine A reacquired the lock.")
55		reentrantLock.Unlock()
56		fmt.Println("Goroutine A released one of its locks.")
57		reentrantLock.Unlock()
58		fmt.Println("Goroutine A fully released the lock.")
59	}()
60
61	// 等待上面的 goroutine 执行完成...
62}
63

上述代码定义了一个名为 ReentrantMutex 的结构体,它在内部维护了一个原始的 Mutex 和一些状态变量来跟踪持有锁的 goroutine 以及该 goroutine 对锁的递归获取次数。当同一个 goroutine 请求已经持有的锁时,递归计数器增加;当释放锁时,如果递归计数器大于 1,则递减而不完全释放锁。

请注意,这里的 getGoroutineID() 函数是虚构的,因为在 Go 语言中并没有直接暴露 Goroutine 的唯一标识符。在实际实现中,可能需要通过其他方式来追踪锁的拥有者,例如通过自定义上下文或者关联特定的数据结构等。

go lru缓存

在Go语言中,要实现一个LRU(Least Recently Used)缓存,可以使用标准库或第三方库。这里展示如何使用 container/list 和 map 结合来手动实现一个简单的LRU缓存:


Go

1package main
2
3import (
4	"container/list"
5	"fmt"
6)
7
8// LRUCache 结构体定义了一个容量限制的LRU缓存
9type LRUCache struct {
10	capacity int
11	cache    map[int]*list.Element
12	list     *list.List
13}
14
15// CacheItem 用于存储键值对和其在链表中的位置
16type CacheItem struct {
17	key   int
18	value interface{}
19}
20
21// NewLRUCache 创建一个新的LRU缓存实例
22func NewLRUCache(capacity int) *LRUCache {
23	return &LRUCache{
24		capacity: capacity,
25		cache:    make(map[int]*list.Element),
26		list:     list.New(),
27	}
28}
29
30// Get 方法从缓存中获取键对应的值,如果存在则更新其为最近使用
31func (c *LRUCache) Get(key int) (interface{}, bool) {
32	if elem, ok := c.cache[key]; ok {
33		c.list.MoveToFront(elem)
34		return elem.Value.(*CacheItem).value, true
35	}
36	return nil, false
37}
38
39// Put 方法将键值对添加到缓存,并根据容量调整缓存内容
40func (c *LRUCache) Put(key int, value interface{}) {
41	if elem, ok := c.cache[key]; ok {
42		elem.Value.(*CacheItem).value = value
43		c.list.MoveToFront(elem)
44	} else {
45		if c.list.Len() >= c.capacity {
46			delete(c.cache, c.list.Back().Value.(*CacheItem).key)
47			c.list.Remove(c.list.Back())
48		}
49		newElem := c.list.PushFront(&CacheItem{key: key, value: value})
50		c.cache[key] = newElem
51	}
52}
53
54// 示例代码
55func main() {
56	cache := NewLRUCache(3)
57
58	cache.Put(1, "one")
59	cache.Put(2, "two")
60	cache.Put(3, "three")
61
62	fmt.Println(cache.Get(1)) // 输出:("one", true)
63	cache.Put(4, "four")      // 此时,键为1的项因超过容量而被移除
64	fmt.Println(cache.Get(1)) // 输出:(nil, false)
65	fmt.Println(cache.Get(2)) // 输出:("two", true)
66	fmt.Println(cache.Get(3)) // 输出:("three", true)
67	fmt.Println(cache.Get(4)) // 输出:("four", true)
68}

这个实现仅作为基础示例,实际应用中可能需要考虑更多边界条件以及线程安全问题。

另外,Go社区有一些成熟的第三方包如 github.com/hashicorp/golang-lru,它提供了更高效且易于使用的LRU缓存实现。

你可能感兴趣的:(golang,java,前端,后端)