范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

一种比读写锁更快的锁,还不赶紧认识一下

  摘要:一起来聊聊在高并发环境下比ReadWriteLock更快的锁——StampedLock。
  本文分享自华为云社区《【高并发】高并发场景下一种比读写锁更快的锁,看完我彻底折服了!!(建议收藏)-云社区-华为云》,作者:冰 河 。 什么是StampedLock?
  ReadWriteLock锁允许多个线程同时读取共享变量,但是在读取共享变量的时候,不允许另外的线程多共享变量进行写操作,更多的适合于读多写少的环境中。那么, 在读多写少的环境中,有没有一种比ReadWriteLock更快的锁呢?
  答案当然是有!那就是我们今天要介绍的主角——JDK1.8中新增的StampedLock!没错,就是它!
  StampedLock与ReadWriteLock相比,在读的过程中也允许后面的一个线程获取写锁对共享变量进行写操作,为了避免读取的数据不一致,使用StampedLock读取共享变量时,需要对共享变量进行是否有写入的检验操作,并且这种读是一种乐观读。
  总之,StampedLock是一种在读取共享变量的过程中,允许后面的一个线程获取写锁对共享变量进行写操作,使用乐观读避免数据不一致的问题,并且在读多写少的高并发环境下,比ReadWriteLock更快的一种锁。 StampedLock三种锁模式
  这里,我们可以简单对比下StampedLock与ReadWriteLock,ReadWriteLock支持两种锁模式: 一种是读锁,另一种是写锁 ,并且ReadWriteLock允许多个线程同时读共享变量,在读时,不允许写,在写时,不允许读,读和写是互斥的,所以,ReadWriteLock中的读锁,更多的是指悲观读锁。
  StampedLock支持三种锁模式: 写锁、读锁 (这里的读锁指的是悲观读锁) 和乐观读 (很多资料和书籍写的是乐观读锁,这里我个人觉得更准确的是乐观读,为啥呢?我们继续往下看啊)。其中,写锁和读锁与ReadWriteLock中的语义类似,允许多个线程同时获取读锁,但是只允许一个线程获取写锁,写锁和读锁也是互斥的。
  另一个与ReadWriteLock不同的地方在于:StampedLock在获取读锁或者写锁成功后,都会返回一个Long类型的变量,之后在释放锁时,需要传入这个Long类型的变量。例如,下面的伪代码所示的逻辑演示了StampedLock如何获取锁和释放锁。 public class StampedLockDemo{  //创建StampedLock锁对象  public StampedLock stampedLock = new StampedLock();  //获取、释放读锁  public void testGetAndReleaseReadLock(){  long stamp = stampedLock.readLock();  try{  //执行获取读锁后的业务逻辑  }finally{  //释放锁  stampedLock.unlockRead(stamp);  }  }  //获取、释放写锁  public void testGetAndReleaseWriteLock(){  long stamp = stampedLock.writeLock();  try{  //执行获取写锁后的业务逻辑。  }finally{  //释放锁  stampedLock.unlockWrite(stamp);  }  } }
  StampedLock支持乐观读,这是它比ReadWriteLock性能要好的关键所在。  ReadWriteLock在读取共享变量时,所有对共享变量的写操作都会被阻塞。而StampedLock提供的乐观读,在多个线程读取共享变量时,允许一个线程对共享变量进行写操作。
  我们再来看一下JDK官方给出的StampedLock示例,如下所示。 class Point {  private double x, y;  private final StampedLock sl = new StampedLock();  void move(double deltaX, double deltaY) { // an exclusively locked method  long stamp = sl.writeLock();  try {             x += deltaX;             y += deltaY;  } finally {  sl.unlockWrite(stamp);  }  }  double distanceFromOrigin() { // A read-only method  long stamp = sl.tryOptimisticRead();  double currentX = x, currentY = y;  if (!sl.validate(stamp)) {             stamp = sl.readLock();  try {  currentX = x;  currentY = y;  } finally {  sl.unlockRead(stamp);  }  }  return Math.sqrt(currentX * currentX + currentY * currentY);  }  void moveIfAtOrigin(double newX, double newY) { // upgrade  // Could instead start with optimistic, not read mode  long stamp = sl.readLock();  try {  while (x == 0.0 && y == 0.0) {  long ws = sl.tryConvertToWriteLock(stamp);  if (ws != 0L) {                     stamp = ws;                     x = newX;                     y = newY;  break;  }  else {  sl.unlockRead(stamp);                     stamp = sl.writeLock();  }  }  } finally {  sl.unlock(stamp);  }  } }
  在上述代码中,如果在执行乐观读操作时,另外的线程对共享变量进行了写操作,则会把乐观读升级为悲观读锁,如下代码片段所示。 double distanceFromOrigin() { // A read-only method  //乐观读  long stamp = sl.tryOptimisticRead();  double currentX = x, currentY = y;  //判断是否有线程对变量进行了写操作  //如果有线程对共享变量进行了写操作  //则sl.validate(stamp)会返回false  if (!sl.validate(stamp)) {  //将乐观读升级为悲观读锁         stamp = sl.readLock();  try {  currentX = x;  currentY = y;  } finally {  //释放悲观锁  sl.unlockRead(stamp);  }  }  return Math.sqrt(currentX * currentX + currentY * currentY); }
  这种将乐观读升级为悲观读锁的方式相比一直使用乐观读的方式更加合理,如果不升级为悲观读锁,则程序会在一个循环中反复执行乐观读操作,直到乐观读操作期间没有线程执行写操作,而在循环中不断的执行乐观读会消耗大量的CPU资源,升级为悲观读锁是更加合理的一种方式。 StampedLock实现思想
  StampedLock内部是基于CLH锁实现的,CLH是一种自旋锁,能够保证没有"饥饿现象"的发生,并且能够保证FIFO(先进先出)的服务顺序。
  在CLH中,锁维护一个等待线程队列,所有申请锁,但是没有成功的线程都会存入这个队列中,每一个节点代表一个线程,保存一个标记位(locked),用于判断当前线程是否已经释放锁,当locked标记位为true时, 表示获取到锁,当locked标记位为false时,表示成功释放了锁。
  当一个线程试图获得锁时,取得等待队列的尾部节点作为其前序节点,并使用类似如下代码判断前序节点是否已经成功释放锁: while (pred.locked) {  //省略操作  }
  只要前序节点(pred)没有释放锁,则表示当前线程还不能继续执行,因此会自旋等待;反之,如果前序线程已经释放锁,则当前线程可以继续执行。
  释放锁时,也遵循这个逻辑,线程会将自身节点的locked位置标记为false,后续等待的线程就能继续执行了,也就是已经释放了锁。
  StampedLock的实现思想总体来说,还是比较简单的,这里就不展开讲了。 StampedLock的注意事项
  在读多写少的高并发环境下,StampedLock的性能确实不错,但是它不能够完全取代ReadWriteLock。在使用的时候,也需要特别注意以下几个方面。 StampedLock不支持重入
  没错,StampedLock是不支持重入的,也就是说,在使用StampedLock时,不能嵌套使用,这点在使用时要特别注意。 StampedLock不支持条件变量
  第二个需要注意的是就是StampedLock不支持条件变量,无论是读锁还是写锁,都不支持条件变量。 StampedLock使用不当会导致CPU飙升
  这点也是最重要的一点,在使用时需要特别注意:如果某个线程阻塞在StampedLock的readLock()或者writeLock()方法上时,此时调用阻塞线程的interrupt()方法中断线程,会导致CPU飙升到100%。例如,下面的代码所示。 public void testStampedLock() throws Exception{  final StampedLock lock = new StampedLock();  Thread thread01 = new Thread(()->{  // 获取写锁  lock.writeLock();  // 永远阻塞在此处,不释放写锁  LockSupport.park();  });     thread01.start();  // 保证thread01获取写锁  Thread.sleep(100);  Thread thread02 = new Thread(()->  //阻塞在悲观读锁  lock.readLock()  );     thread02.start();  // 保证T2阻塞在读锁  Thread.sleep(100);  //中断线程thread02  //会导致线程thread02所在CPU飙升     thread02.interrupt();     thread02.join(); }
  运行上面的程序,会导致thread02线程所在的CPU飙升到100%。
  这里,有很多小伙伴不太明白为啥LockSupport.park();会导致thread01会永远阻塞。这里,冰河为你画了一张线程的生命周期图,如下所示。
  这下明白了吧?在线程的生命周期中,有几个重要的状态需要说明一下。 NEW:初始状态,线程被构建,但是还没有调用start()方法。 RUNNABLE:可运行状态,可运行状态可以包括:运行中状态和就绪状态。 BLOCKED:阻塞状态,处于这个状态的线程需要等待其他线程释放锁或者等待进入synchronized。 WAITING:表示等待状态,处于该状态的线程需要等待其他线程对其进行通知或中断等操作,进而进入下一个状态。 TIME_WAITING:超时等待状态。可以在一定的时间自行返回。 TERMINATED:终止状态,当前线程执行完毕。
  看完这个线程的生命周期图,知道为啥调用LockSupport.park();会使thread02阻塞了吧?
  所以,在使用StampedLock时,一定要注意避免线程所在的CPU飙升的问题。那如何避免呢?
  那就是使用StampedLock的readLock()方法或者读锁和使用writeLock()方法获取写锁时,一定不要调用线程的中断方法来中断线程,如果不可避免的要中断线程的话,一定要用StampedLock的readLockInterruptibly()方法获取可中断的读锁和使用StampedLock的writeLockInterruptibly()方法获取可中断的悲观写锁。
  最后,对于StampedLock的使用,JDK官方给出的StampedLock示例本身就是一个最佳实践了,小伙伴们可以多看看JDK官方给出的StampedLock示例,多多体会下StampedLock的使用方式和背后原理与核心思想。
  点击下方,第一时间了解华为云新鲜技术~
  华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云

23城首套房贷利率放宽!专家重在满足刚需购房房贷利率有望再降。9月29日,央行银保监会发布阶段性放宽部分城市首套住房贷款利率下限(下称通知),决定阶段性调整差别化住房信贷政策。通知显示,对于2022年68月份新建商品住宅销售苹果重组AppleCar团队再次造车,预计2025年实现量产根据9月30日中国台湾媒体报道称,苹果公司将重新组建AppleCar团队,再次启动打造新能源电动车的泰坦计划,预计将在2025年实现量产上市。有业内分析认为,之前的AppleCar惠州富豪朱坤华36岁创立硕贝德,44岁公司上市,成手机天线之王朱坤华01朱坤华,1968年4月27日出生在广东惠州,小时候学习用功,非常懂事,家里兄弟多,朱坤华和兄弟朱旭东朱旭华感情深厚。朱坤华从广东省电子技术学校无线电专业毕业后,分配到惠州世界上最有钱的人贝佐斯(离婚分财后仍稳居世界第一)虽然按照福克斯的报告,目前全球有至少2千名亿万富翁,但实际上世界上最有钱的人身价已经超过了千亿元,他就是美国电商巨头亚马逊的老板杰夫贝佐斯,虽然他在离婚后,财富有受到影响,但依然稳养老金五个好消息出台,每个都关系到养老金的待遇,一起来看看点击上方收听全文养老金五个好消息出台,每个都关系到养老金的待遇,一起来看看。在当今社会,无论是上班族还是自由职业者,都会自己掏钱买保险,因为五险一金的实际作用对我们普通人来说是非常高端车市场遭冲击,智能化概念红利退潮,小牛电动面临困局受新国标影响,电动二轮车市场正迎来新一波换购潮,这对电动车企业来说,面临着新的红利期。与雅迪爱玛等行业巨头飘红的财报相比,被称二轮特斯拉的行业新贵小牛电动却交出了一份令投资者心情复为何会出现社保挂靠代缴?如何杜绝这种行为?有效治理社保挂靠代缴社保挂靠代缴反映出我国基本公共服务不足的问题。经过半年多专项整治,社保挂靠代缴现象基本得到了遏制。对监管部门来说,要堵住漏洞,也要疏通引导,还要强化服务。对通过比亚迪市值首破万亿,三款新车,亮相欧洲发布会近日,全球车企市值排名出炉,比亚迪市值首破万亿,跻身全球前三!德系汽车品牌市值被中国车企反超,这样的事实让德国车企如坐针毡,据悉某德国汽车品牌当机立断,决定拿出400亿欧元开发纯电酱油大王海天味业深陷舆论危机最近,因海天味业两张配料表所含添加剂的话题在各类平台异常爆火,对食品添加剂的使用和中国食品安全的讨论也是异常的火爆。有细心的网友发视频称,海天酱油产品的配料表中小麦天然原料谷氨酸钠人类发展遇到可怕敌人,看不见摸不着,但却一直存在身边人类文明发展到今天,我们人类可以毫不夸张的说已经成为了地球上最强大的生物,哪怕恐龙再次复活,人类的核武器可以让这些恐龙再毁灭一次。而地球上的其他生物要不然被人类关押在了动物园里面,北溪事件的幕后黑手,危害了世界几百年,曾使一个人类种族灭绝头条创作挑战赛俄媒9月30日最新报道,普京在克宫发表讲话指责盎格鲁撒克逊人对北溪天然气管道进行破坏,犹太人日耳曼人欧罗巴人大多数人都知道,盎撒人指的是谁?又生活在哪些国家呢?为什么