JAVA中各种锁的对比

在前面的几篇日志都记录了这些锁的使用方式和部分源码

synchronized实现方式及锁优化

ReentrantLock实现及AQS简析

ReentrantReadWriteLock使用及源码分析

StampedLock使用方式

ReentrantLock使用对比Synchronized

这篇文章主要是介绍在实际开发场景中,我们应该如何选择锁,以及哪种锁它的性能会相对而言比较好一些 

synchronized 关键字它的底层实现,是通过hotspot虚拟机内置实现的,在对某个共享资源进行加锁之后,只能允许一个线程进行访问,其他线程只能在对象锁上的等待池中进行等待,并且是不可中断,无休止等待下去,只有等持有锁的线程执行结束,会随机唤醒等待池中的某个线程去执行。所以synchronized所提供的是一个排它锁,可重入锁,非公平锁。synchronized在1.6之后就对锁的实现进行了优化,大部分情况下synchronized的性能还是可以的。

ReentrantLock 可重入锁,它的实现是AQS+CAS一段代码来实现的,CAS操作是通过unsafe提供的原子操作,来对状态进行更新,相较于synchronized提供的锁,ReentrantLock提供的锁,有了更细粒度的操作,比如可以知道当前线程是否获取到了锁,如果没有获取到锁,那么在多久之后,可以进行终止等待,避免了无休止的等到操作。ReentrantLock提供的锁是,排它锁,可重入的,可以是公平也可以使非公平 (公平锁相对于非公平锁会消耗一点性能,因为每次在尝试获取到锁的时候,都需要来计算是否要将线程放入等待队列中去)

ReentrantReadWriteLock 读写锁 ,实现方式也是AQS+CAS 代码实现,它的不同就是对于共享资源,如果我们只是读取的话,可以多个线程同时访问,如果对共享资源进行修改的话,同一时间只能有一个线程持有,当然如果有线程正在读取共享资源,写线程必须要进行等待,这一点也正是读写线程速度相对来说比较慢的一个原因,因为在实际场景当中,我们都是读多写少,但是在读的时候不能写,也就意味着写操作会一直阻塞直到所有读操作结束。

StampedLock 读写锁 由于我们大部分场景都是读多写少,JDK1.8提供了一个全新的读写锁,其实现方式是通过CAS+双向链表,他的特点就是提供了乐观读,乐观是相对于数据库中悲观锁和乐观锁而言的,在读取的时候,先不加锁,也不通过CAS去修改数据,当乐观锁失败后,转为悲观锁,进行加锁操作,而StampedLock最棒的地方就是乐观锁,即乐观读取,因为我们大部分时候是读多写少,就不需要通过CAS来修改状态,这样进一步提高了性能。StampedLock是不可重入的,但是其性能不会因为不可重入受影响。缺点是:使用时每个锁的获取和释放都基于票据,如果一不小心,代码逻辑或者票据错误很容易造成死锁

以上介绍了几种锁各自的特点使用场景及缺点,以下进行总结:

1、ReentrantReadWriteLock 在任何场景下都不推荐使用,因为其相关的功能通能被StampedLock锁代替,并且速度更快

2、StampedLock 在线程竞争不激烈的情况下,他的读写锁是比自身的乐观锁要快的,当线程数量很大的时候,乐观锁的优势就体现出来了

3、synchronized锁整体来说,性能也不错,在对性能要求极其严格的情况下,需要写具体的代码进行压测对比。

综上所述,还是需要写测试代码,在生产环境进行压测,找到最适合的锁

参考:Java 8:StampedLock、ReadWriteLock以及synchronized的比较

打赏

您看完此文章的心情是

  • 0人

  • 鼓掌

    0人

  • 草泥马

    0人

  • 愤怒

    0人

  • 鄙视

    0人

评论

    暂无评论...