java并发编程——锁机制

  • 第一部分:synchronized和volatile

锁机制用来保护对象的一致性以及操做的原子性,是实现线程安全的重要手段。线程安全涉及到对象两个重要的状态:共享性和可变性。若是对象是不可变的、线程私有的那么它必定是线程安全的。因此说,只有在共享的、可变的对象上面进行操做时才须要加锁,以保障线程安全。volatile和synchronized是java 5.0以前最先协调对象共享的机制。下面咱们将分别介绍他们:java

  • synchronized

synchronized用来形容方法或者代码块,是java提供的最先的锁机制,支持重入锁。关于synchronized的详细解析文章有不少,这里列举几个注意事项:缓存

第一,使用synchronized关键字时必定要尽量的缩小范围,尽量的在方法块里须要锁的地方使用,而不是直接用来修饰整个方法。这须要咱们对方法里面的操做进行分析,哪些须要加锁,哪些不须要加锁,只在须要锁的地方加锁,这样便可以提升程序的效率,同时开放调用方法也减小了线程安全的隐患。安全

第二,synchronized提供的锁机制是粗粒度的,当有线程访问当前对象的synchronized方法或代码块时,其余线程只能等待当前操做结束才能够访问。这听上去彷佛存在必定的性能问题,但java 6.0之后synchronized在并发环境下性能获得了大幅提高,所以建议尽量的使用synchronized,除非synchronized知足不了业务需求。并且synchronized使用时无需释放锁,并且JVM还提供了专门的优化支持,所以即便synchronized是古老的锁,可是它依然适用于绝大多数场景。数据结构

  • volatile

volatile用来修饰变量保证其可见性。可见性是一种复杂的属性,volatile变量不会被缓存在寄存器或者其余处理器不可见的地方,在读取volatile变量时返回的必定是最新写入的值。volatile不是线程安全的,他不能替代锁,它只在特定的场景下使用,使用时要很是当心。如下场景可使用volatile:并发

第一,对变量的写入不依懒于变量当前的值,或者你能确保只有单个线程更新变量的值;性能

第二,该变量不会与其它状态变量一块儿归入不变性条件中;优化

第三,在访问变量时不须要加锁。spa

  • 第二部分:ReentrantLock

ReentrantLock是一个可重入的互斥锁,继承自Lock接口。若是说synchronized是隐式锁的话,那么ReentrentLock就是显式锁。锁的申请、使用、释放都必须显式的申明。Lock接口提供了如下方法:线程

public interface Lock{
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time,TimeUnit unit) throws InterruptedException;
    void unLock();
    Condition newCondition();
}

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。那既然如此,为何还要建立一个相似的锁机制呢?内置锁虽然好用,可是缺少一些灵活性,而ReentrantLock则能够弥补这些不足。code

第一,轮询锁与定时锁。tryLock方法实现了可定时的与可轮询的锁实现。与synchronized相比它有更完善的错误恢复机制。内置锁中死锁是一类严重的错误,只能重启程序。而ReentrantLock可使用可定时或者轮询的锁,它会释放已得到的锁,而后再尝试得到全部的锁。在实现具备时间限制的操做时,定时锁也很是也用。若是操做在给定时间内不能给出结果那么就会使程序提早结束。

第二,可中断锁获取操做。lockInterruptibly方法可以在得到锁的同时保持对中断的响应。并且因为它包含在Lock中,所以无需建立其余类型的不可中断阻塞机制。

  • 第三部分:ReadWriteLock

ReentrantReadWriteLock实现了一种标准的互斥读写锁,继承自ReadWriteLock。ReadWriteLock接口包含如下方法:

public interface ReadWriteLock{
    Lock readLock();
    Lock WriteLock();
}

ReentrantReadWriteLock咱们能够这样理解:当执行读操做的时候能够多个线程并发访问;当执行写操做的时候,只能够同时被一个线程访问。因此它使用的场景是读操做多而写操做少的并发场景。此外,ReentrantReadWriteLock还能够设置是否为公平锁,是公平锁的话则能够按照排队的顺序获取锁,非公平锁的话则是随机得到。

  • 第四部分:锁的公平性

上一节讲到了ReentrantReadWriteLock实现了公平锁和非公平锁两种模式。在公平锁模式下线程按照请求的顺序来得到锁,而非公平模式下则能够插队。咱们的指望是全部的锁都是公平的,毕竟插队是一种很差的行为。但实际上非公平锁比公平锁有着更高的并发效率。假设线程A持有一个锁,而且线程B也请求这个锁,因为该锁被线程A占有,因此B线程挂起,当A使用结束时释放锁,此时唤醒B,B须要从新申请得到锁。若是同时线程C也请求这个锁,而且C极可能在B得到锁以前已经得到、使用并释放了锁。这样就实现了共赢,B线程得到的锁没有延迟同时线程C也获得了执行,这提升了程序的吞吐率。

总结:与内置锁相比,显式锁提供了一些扩展功能,在处理锁的不可用方面有着更高的灵活性,而且对队列有着更好的控制。可是显式锁没法替换synchronized,只有在synchronized没法知足需求时才会使用它。读写锁容许多个读线程并发的访问被保护对象,当访问以读取操做为主的数据结构时,能提升程序的伸缩性。

相关文章
相关标签/搜索