在多线程中,锁是一种最常用的同步工具,下面详细讲讲带有锁字的一些术语:
1.锁的具体实现原理:
(1).互斥锁(Mutex)
用一个“互斥锁”的对象,任一时刻,只有一个线程能访问这个对象,也就是把代码分成一个个临界区域。在Linux下伪代码如下:
pthread_mutex_t mutex; pthread_mutex_init (&mutex, NULL); /*初始化锁*/ pthread_mutex_lock(&mutex); /*获取互斥锁,也就是加锁*/ ... /*临界区*/ pthread_mutex_unlock(&mutex); /*解锁互斥锁*/
如图,中间的临界区就实现了加锁,每次只有一个线程才能访问。基本上我们线程同步用的都是互斥锁。
2.自旋锁(Spin Lock)
跟互斥锁类型,都是为了实现互斥访问某个对象,但是互斥锁在资源被占用的时候会进入睡眠,而自旋锁则会一直循环去探测能否获取资源,在某种意义上,就是一直while循环探测。所以自旋锁很容易就占用cpu过多,但是不需要线程的休眠调度等,会效率比较高,适用加锁时间很短的情况。
3.读写锁(RWLock)
读写锁就是一个特殊的自旋锁,只是把需要进入临界区的访问者分成读者和写者,写者是排他的,但是允许多个读者同时存在。也就是说如果没有读者和写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,即使有其他读者,这个读者也可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。
互斥锁跟自旋锁一些优缺点:
Spinlock优点:没有昂贵的系统调用,一直处于用户态,执行速度快。
Spinlock缺点:一直占用cpu,而且在执行过程中还会锁bus总线,锁总线时其他处理器不能使用总线。
Mutex优点:不会忙等,得不到锁会sleep。
Mutex缺点:sleep时会陷入到内核态,需要昂贵的系统调用。
2.锁的一些分类
1.递归锁(Recursive Lock)和非递归锁(Non-Recursive Lock)
两个唯一的区别就是一个线程可以多次获取一个递归锁,而不会导致死锁,而多次获取一个非递归锁就会导致死锁。如下:
mutex_init(&mutex); void fun() { lock(&mutex); //do something1 fun2(); unlock&mutex); } void fun2() { lock(&mutex); //do something2 unlock(&mutex); }
在fun()函数加锁调用了fun2(),而fun2()也加了锁,如果是非递归锁,那么fun获取了mutex,fun2再去获取mutex,就会导致死锁了。
2.乐观锁(Optimistic Lock)和悲观锁(Pessimistic Lock)
主要是用于关系型数据库存储。乐观锁,大多是基于数据版本(Version)记录机制实现,即为数据增加一个版本标识。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。以后提
3.死锁(Dead Lock)和活锁(Live Lock)
死锁大家应该都比较清楚,简单地说,就是进入死等待,例如P1占用了资源A,请求资源B,而P2占用了资源B,请求资源A,这样就导致双方一直在等待。而活锁就是资源一直轮不到自己使用,导致一直饥饿,例如如果事务T1封锁了数据R,事务T2请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求,...,T2有可能永远等待,这就是活锁的情形。
主要是一些概念的介绍,具体的还需要具体去用了才能体会。
谢谢指教。