转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120750932
本文出自【赵彦军的博客】

Java线程安全StampedLock
Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock
Java线程安全集合总结
Java原子操作Atomic

文章目录

  • 前言
  • Lock
  • ReentrantLock
    • 公平锁/非公平锁
    • 超时机制
    • 可重入锁
  • 读写锁 ReentrantReadWriteLock
    • 源码结构
    • 总结
    • 示例

前言

java5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁的功能,它提供了与synchronized关键字类似的同步功能。

既然有了synchronized这种内置的锁功能,为何要新增Lock接口?先来想象一个场景:手把手的进行锁获取和释放,先获得锁A,然后再获取锁B,当获取锁B后释放锁A同时获取锁C,当锁C获取后,再释放锁B同时获取锁D,以此类推,这种场景下,synchronized关键字就不那么容易实现了,而使用Lock却显得容易许多。

Lock

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

public class ReentrantLock implements Lock, java.io.Serializable {}

使用如下:

public class LockTest {Lock lock = new ReentrantLock();public void run(){//获取锁 lock.lock();//doSomething//释放锁lock.unlock();}
}

使用起来,也非常简单,首先 lock.lock(); 获取锁,然后去执行自己的逻辑,最后调用 lock.unlock(); 释放锁。

但是这里有个问题,如果我们的逻辑发生异常,那就永远无法释放锁,所以我们优化一下,把释放锁的逻辑放在 finally 块中,如下

public class LockTest {Lock lock = new ReentrantLock();public void run(){//获取锁lock.lock();try {//doSomething}catch (Exception e){}finally {//释放锁lock.unlock();}}
}

公平锁/非公平锁

  • 非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取;

  • 公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁。

ReentrantLock 主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。

  • CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现

ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。

AbstractQueuedSynchronizer 简称AQS

我们来看一下 ReentrantLock 构造函数

   public ReentrantLock() {sync = new NonfairSync();}/*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

构造支持传入 fair 来指定是否是公平锁

  • FairSync() 公平锁
  • NonfairSync() 非公平锁

如果使用无参构造函数,则是非公平锁。

超时机制

ReetrantLock的tryLock(long timeout, TimeUnit unit) 提供了超时获取锁的功能。它的语义是在指定的时间内如果获取到锁就返回true,获取不到则返回false。这种机制避免了线程无限期的等待锁释放。

可重入锁

  • 可重入锁。可重入锁是指同一个线程可以多次获取同一把锁。ReentrantLock和synchronized都是可重入锁。

  • 可中断锁。可中断锁是指线程尝试获取锁的过程中,是否可以响应中断。synchronized是不可中断锁,而ReentrantLock则提供了中断功能。

  • 公平锁与非公平锁。公平锁是指多个线程同时尝试获取同一把锁时,获取锁的顺序按照线程达到的顺序,而非公平锁则允许线程“插队”。synchronized是非公平锁,而ReentrantLock的默认实现是非公平锁,但是也可以设置为公平锁。

读写锁 ReentrantReadWriteLock

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。

在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。

针对这种场景,JAVA的并发包提供了读写锁 ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁,描述如下:

线程进入读锁的前提条件:

  • 没有其他线程的写锁,

  • 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。

线程进入写锁的前提条件:

  • 没有其他线程的读锁

  • 没有其他线程的写锁

而读写锁有以下三个重要的特性:

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。

源码结构

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {/** 读锁 */private final ReentrantReadWriteLock.ReadLock readerLock;/** 写锁 */private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;/** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock() {this(false);}/** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}/** 返回用于写入操作的锁 */public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }/** 返回用于读取操作的锁 */public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {}static final class NonfairSync extends Sync {}static final class FairSync extends Sync {}public static class ReadLock implements Lock, java.io.Serializable {}public static class WriteLock implements Lock, java.io.Serializable {}
}

1、类的继承关系

public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable {}

说明:可以看到,ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。

2、ReentrantReadWriteLock有五个内部类,五个内部类之间也是相互关联的。内部类的关系如下图所示。


说明:如上图所示,Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类(通过构造函数传入的布尔值决定要构造哪一种Sync实例);ReadLock实现了Lock接口、WriteLock也实现了Lock接口。

总结

  • 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。

  • 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。

仔细想想,这个设计是合理的:因为当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。

综上:

一个线程要想同时持有写锁和读锁,必须先获取写锁再获取读锁;写锁可以“降级”为读锁;读锁不能“升级”为写锁。

示例

public class LockTest {ReadWriteLock lock = new ReentrantReadWriteLock();Lock readLock = lock.readLock();Lock writeLock = lock.writeLock();}

Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock相关推荐

  1. java 线程锁Lock

    文章目录 说明 分享 资料 synchronized与Lock对比 Lock Lock 接口方法 实现类 ReentrantLock ReadWriteLock ReadWriteLock 接口方法 ...

  2. java线程系列---读写锁ReentrantReadWriteLock

    获取顺序 此类不会将读取者优先或写入者优先强加给锁定访问的排序.但是,它确实支持可选的公平 策略.当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入.当释放写入锁定后,将写入锁定分配给等待时 ...

  3. Java线程安全StampedLock

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120854573 本文出自[赵彦军的博客] Java线程安全StampedLock ...

  4. Java线程安全集合总结

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120749064 本文出自[赵彦军的博客] Java线程安全StampedLock ...

  5. 【java线程】锁机制:synchronized、Lock、Condition

    [Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...

  6. java同步关键词解释、synchronized、线程锁(Lock)

    1.java同步关键词解释 21.1 synchronized synchronized是用来实现线程同步的!!! 加同步格式: synchronized( 需要一个任意的对象(锁) ){ 代码块中放 ...

  7. 深圳Java培训:Lock线程锁

    深圳Java培训:Lock线程锁 1:synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 在上面一篇文章中 ...

  8. Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock

    在JDK5里面,提供了一个Lock接口.该接口通过底层框架的形式为设计更面向对象.可更加细粒度控制线程代码.更灵活控制线程通信提供了基础.实现Lock接口且使用得比较多的是可重入锁(Reentrant ...

  9. java lock 效率_工作常用4种Java线程锁的特点,性能比较、使用场景

    多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升.虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求. 使用多线程的理由之一是和进程相比,它是一种非常花 ...

最新文章

  1. python使用matplotlib可视化线图(line plot)、设置X轴坐标的下限和上限数值(setting the lower and upper bound of the x axis)
  2. shell中的命令替换和变量替换
  3. 5. 连续时间马氏过程-强Markov族
  4. html 静态资源缓存时间,.htaccess设置静态资源缓存(即浏览器缓存)
  5. BZOJ.4555.[HEOI2016TJOI2016]求和(NTT 斯特林数)
  6. SAP 电商云 Spartacus UI 同 SAP Customer Data Cloud 的集成
  7. 网络原理题+复习资料
  8. 简洁版即时聊天---I/O多路复用使用
  9. Bootstrap3 滚动监听插件的调用方式
  10. 终于有人把云计算、大数据和人工智能讲明白了! (2)
  11. Android StrictMode类使用实例
  12. 软件测试 接口测试 入门Jmeter 接口关联 提取器 断言 与fiddler配合使用 使Jmeter录制和创建脚本 操作数据库 持续集成测试
  13. python可视化图表生成(一)
  14. 如何在vs2020编译c语言程序,如何在使用WIN10 Visual Studio 2015 编译FlightGear源码(2020.1.1版本)(示例代码)...
  15. 学 Python 知识点其实特简单, “内置函数“ 思维导图来了
  16. 知识竞赛中如何按抢答器才能最先抢到
  17. matlab 浮动波动率,Matlab计算股票价格波动率
  18. 网络:IP基础知识总结
  19. 第五章 数组及排序 ① 笔记
  20. html中显示框框中对勾,如何打出方框里有对勾

热门文章

  1. jmeter展示内存cpu_基于Docker的jmeter弹性压测(2)监控
  2. 三十六、 SQL基本语法
  3. K折交叉验证和pipeline
  4. 四十四、Python中的statsmodels模块
  5. 额外篇 | ggplot (下)
  6. opencv 裁剪 java_OpenCV绘制轮廓和裁剪
  7. 博士申请 | ​英属哥伦比亚大学李霄霄助理教授招收全奖博士生、硕士生
  8. 爱思唯尔云论坛:疫情挑战下的大数据与人工智能研究
  9. Greenplum【集群搭建 01】局域网 CentOS 7.9.2009 环境 GreenPlum 6.13.0 集群规划+配置+安装+内核参数调整(应用实例分享)
  10. SpringBoot 项目war包部署 配置外置tomcat方法