研究生阶段一直使用C++,到工作时,才接触到Java。写了这么多年的多线程程序,觉得对于互斥(注意,不是同步哦)的各种锁有必要做个总结。

 

这里我想将Windows,Linux和Java JVM三种环境中使用锁的环境及虽然将Windows,Linux和Java JVM放在一起比较是有些不合适的,但是对基于Windows操作系统C++,Linux操作系统C++及Java程序而言,在应用层面上来说,这种横向比较对于加深对于多线程互斥中锁应用上的理解,是有意义的。如果有不正确的地方,欢迎指正。

 

Windows

有InterLock系列函数,CRITICAL_SECTION临界区,SRWLock读写锁,Mutex互斥量

 

Linux

有atomic_t,Mutex互斥量,,rwlock读写锁

 

Java JVM

有Atomic原子操作,synchronized同步关键字,ReentrantLock,ReentrantReadWriteLock读写锁

 

 

互斥量

环境

睡眠锁 / 自旋锁

可重入 / 不可重入

用户态 / 内核态

可被中断 / 不可被中断

备注

CRITICAL_SECTION 临界区

Windows

先自旋锁,后睡眠锁

可重入

用户态

 

 

SRWLock 读写锁

Windows

读写锁

不可重入

 用户态

 

 

Mutex 互斥量

Windows

睡眠锁

 不可重入

内核态

 

 

 

 

 

 

 

 

 

spin_lock

Linux

自旋锁

 

 内核

 

 

Mutex 互斥量

Linux

睡眠锁

 

 pthread

 

 

rwlock

Linux

读写锁

 

 pthread

 

 

 

 

 

 

 

 

 

synchronized

JVM

类自旋锁

可重入

JVM 内部锁

 

 

ReentrantLock

JVM

类睡眠锁

可重入

Java 类库的锁

 

 

ReentrantReadWriteLock

JVM

读写锁

 可重入

 Java类库的锁

 

 

 

 

 

 

 

 

 

 

 

 

这里着重说一下,何时使用自旋锁,何时使用读写锁,何时使用睡眠锁。

在临界区范围较小,竞争不强,并且其中没有读写文件之类的内核态操作时,最好使用自旋锁;这是因为,自旋锁是以一种“忙等”的方式抢占锁,如果临界区范围较大,或竞争很强,或者有内核态操作,导致多个线程同时等待一个自旋锁资源,服务器的CPU使用率将会非常高,上下文切换会比较频繁,性能不佳。对于Windows的临界区,微软特别做了优化,它会先自旋一段时间,然后,如果长时间进入不了临界区,则会使用互斥量进入睡眠状态。

在读写数量上差距很大时,(读多写少或者写多读少),使用读写锁;读写锁,大多数情况下都是自旋锁的一个变种。

在临界区范围较大或者临界区其中有内核态操作时,最好使用睡眠锁;睡眠锁在抢占不到锁的情况下,会进入内核态,使自身所在的线程被挂起。在临界区较小时,进入内核态的时间长度很可能大于在临界区中的时间长度,导致性能不佳,但是如果临界区很大,其中操作时间长度大于睡眠锁进入内核态的时间长度,尤其是在竞争很强的情况下,性能相对自旋锁要好些。

 

对于JVM,为什么我会写类自旋锁和类睡眠锁,这是因为,它们的性能情况和在C++下是不一样的,在Java5.0中,synchronized的性能和ReentrantLock的性能的对比与自旋锁和睡眠锁的性能对比类似,但是在Java6.0中则不然,synchronized在竞态的性能有了大幅的提高,使得在竞态情况下,synchronized的性能和ReentrantLock的性能基本一致。

 

最后,也是最重要的是,以上说法都不准确,唯一准确的是在你自己的代码中测试性能的结果。

posted on 2015-06-29 13:53  一个人的天空@  阅读(1773)  评论(1编辑  收藏  举报