TAS 是test and set 的缩写,直白的翻译过来就是比较然后测试。java中的原子类大量使用了TAS操作。通过TAS 我们可以安全并且无阻塞的设置原子变量,不用加锁也能进行线程安全的操作。本文目的不是谈原子变量的使用和实现原理的,这个以后会单独来讲。我们主要来看如何使用TAS操作来实现互斥锁。

首先让我们来看看最简单的一种实现TASLock,废话少说直接上代码:

//test and set lockpublic class TASLock { private AtomicBoolean state = new AtomicBoolean(false); // 加锁 public void lock() { while (state.getAndSet(true)) {  } } // 解锁 public void unlock() { state.set(false); } }

这应该是互斥锁的最简单的实现了吧,加锁和解锁只需要一行有效代码。让我们详细来分析一下TASLock类。TASLock有一个AtomicBoolean类型的字段state,我们用这个字段来标示锁的状态,初始值为false。state为true说明锁已经被某个线程占有,fase则说明锁空闲。AtomicBoolean是布尔值的一个原子类型实现类。关于原子类型的原理和使用场景以后单独写一篇来讲,不是这篇的主要目的。目前只要知道原子类型的方法是线程安全的就ok了。我们再来看一下代码中调用state的两个方法:

(1)getAndSet ,这个方法是设置state的值为参数值,并返回state之前的旧值。

(2)set,这个看方法名就知道了。

这两个方法都是线程安全的,所以不需要同步。通过下面流程图,加锁的原理一目了然。例如有a、b两个线程。a线程先执行lock,此时获取的state是false,所以a线程会直接退出while循环并设置state值true。b线程开始执行lock,此时b线程获取的state值是true,(b也会设置state值为true,不过并没有影响)。所以b会不停的执行getAndSet操作(被阻塞在临界区之外了)。如果此时a执行unlock,则设置state为false,b检测到state为false,会设置state为true并退出while。so easy!

OK,让我们再来看看一种锁TTASLock。依然是先上代码:

/*** * Test test and set lock* */
public class TTASLock {private AtomicBoolean state = new AtomicBoolean(false);// 加锁public void lock() {while (true) {while (state.get()) {// 自旋}if (!state.getAndSet(true)) {break;}}}// 解锁public void unlock() {state.set(false);}
}

TTASlock比TASLock加锁要稍微复杂一下,解锁则是一模一样的。让我们分析一下TTASLock的加锁:

执行lock时,先进入无限循环,然后判断state的值是否为true(此时没有再执行set哦)。如果为true,则一直的循环判断(自旋)。如果state为false,跳出循环并执行getAndSet方法(这一步总是会设置state为true)。如果state原先值为false,跳出无限循环,成功获得锁。否则再次回到判断起点。看一下流程图会更清晰一点:

TTASLock解锁和TALS解锁一样,就是将state设置成false。
两个锁的实现都很简单,逻辑并不是很复杂。这两个锁的主要区别在于加锁上。TASLock和TTASLock加锁从逻辑上来讲,原理是一样的,但是效率上去完全不一样。TTASLock的性能要比TASLock的性能要好的多。这是为什么呢?

这就用上了上一篇讲的那一点点硬件知识。我们先来看一下TTASLock和TASLock加锁上的不同:TTASLock是在get上自旋,一旦符合要求在去执行getandset方法,而TASLock直接在getandset上自旋。让我们用一个例子来说明问题。

TTASLock:有a和b两个线程。a线程执行lock。在执行state.get的时候,第一次是从主存中读取值,然后a将该值放入自己的本地缓存中。a线程发现读取的state值为false,于是跳出循环继续执行state.getAndSet方法。首先a从本地缓存中读取state,发现为false,然后a将state设置为true。一旦state被设置,此时所有线程本地缓存中保存的state值都被置为无效。a成功获得锁。b线程开始执行,b线程先执行get方法,第一次依然是从主存中加载state值并存入本地缓存。b读取的state为true(a设置的),b不停的执行get方法。在a线程改变state之前,b每次读取都是从本地缓存中读取state值(本地旋转),这个过程中是不会消耗总线资源的。a执行解锁操作,设置state为false,所有线程本地缓存中的state值被设置无效。接下来其他线程读取state时都会产生一个cache缺失(不命中),所以b在a执行完unlock后,下一次的get操作又是从主存中读取state值。

TASLock :自旋测试(getandset)每次都会去设置state值,导致其他线程的本地缓存无效。每次都会产生缓存缺失。大量的缓存缺失又会导致总线资源被严重的占用(每次都得从主存中加载值)。更坏的结果本该执行解锁的线程因为总线资源的阻塞而导致被延迟释放锁。所以TASLock性能比TTASLock要低的多。

当然TTASLock也不是完美的加锁方案。因为TTASLock在解锁的时候,会导致其他线程本地缓存中state值无效。所有的线程再次执行get时都会产生缓存缺失,都会从主存中去重新加载,这会导致一阵的总线风暴。虽然产生的流量比TASLock要小,但是在高并发的环境中还是非常可观的。这就需要我们后面要将的如果用队列去取解决缓存的一致性流量问题。

转载于:https://my.oschina.net/clopopo/blog/140479

自旋锁学习系列(2):TAS锁相关推荐

  1. 轻量级锁_一句话撸完重量级锁、自旋锁、轻量级锁、偏向锁、悲观、乐观锁等各种锁 不看后悔系列...

    重量级锁?自旋锁?自适应自旋锁?轻量级锁?偏向锁?悲观锁?乐观锁?执行一个方法咋这么辛苦,到处都是锁. 今天这篇文章,给大家普及下这些锁究竟是啥,他们的由来,他们之间有啥关系,有啥区别. 重量级锁 如 ...

  2. java同步锁优化方案学习笔记(偏向锁,轻量级锁,自旋锁,重量级锁)

    目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向锁 Baised Lock 五,轻量级锁 六,自旋锁 SpinLock 七,重量级锁 八,在应用层提高锁效率的 ...

  3. java学习系列2(并发锁问题-乐观锁与悲观锁以及乐观锁的一种实现方式-CAS)

    Java并发问题--乐观锁与悲观锁以及乐观锁的一种实现方式-CAS 首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别 ...

  4. reentrantlock非公平锁不会随机挂起线程?_【原创】Java并发编程系列16 | 公平锁与非公平锁...

    本文为何适原创并发编程系列第 16 篇,文末有本系列文章汇总. 上一篇提到重入锁 ReentrantLock 支持两种锁,公平锁与非公平锁.那么这篇文章就来介绍一下公平锁与非公平锁. 为什么需要公平锁 ...

  5. 打通JAVA与内核系列之一ReentrantLock锁的实现原理

    简介:写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁).其中synchronized锁是JAVA语言层面提供的能力, ...

  6. Linux内核中的同步原语:自旋锁,信号量,互斥锁,读写信号量,顺序锁

    Linux内核中的同步原语 自旋锁,信号量,互斥锁,读写信号量,顺序锁 rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. Linux 内核中的同步原 ...

  7. 并发编程系列之一:锁的意义

    背景 C/C++语言的并发程序(Concurrent Programming)设计,一直是一个比较困难的话题.很多朋友都会尝试使用多线程编程,但是却很难保证自己所写的多线程程序的正确性.多线程程序,如 ...

  8. 12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁

    小陈:呼叫老王...... 老王:来了来了,小陈你准备好了吗?今天我们来讲synchronized的锁重入.锁优化.和锁升级的原理 小陈:早就准备好了,我现在都等不及了 老王:那就好,那我们废话不多说 ...

  9. 【面试 分布式锁详细解析】续命 自旋锁 看门狗 重入锁,加锁 续命 解锁 核心源码,lua脚本解析,具体代码和lua脚本如何实现

    Redisson实现分布式锁原理 自己实现锁续命 在 controller 里开一个 线程 (可以为 守护线程) 每10秒,判断一个 这个 UUID是否存在,如果 存在,重置为 30秒. 如果不存在, ...

  10. 互斥锁(排它锁、独占锁、写锁、X锁)和共享锁(读锁、S锁) 自旋锁

    共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁,直到已释放所有共享锁.获准共享锁的事务只能读数据,不能修改数据. 排他锁(X锁):如果事务T对数据A加上排他 ...

最新文章

  1. 优秀的PHP程序员至少应该了解PHP代码的优缺点(转)
  2. GridView 模版列编辑状态Dropdownlist 事件
  3. 三种方式实现观察者模式 及 Spring中的事件编程模型
  4. OPK修改操作系统信息 --oobe.xml
  5. c语言十万以内超级素数,用C语言求素数的优化
  6. NTU 课程笔记 :NLP - language model
  7. 你的 CRF 层的学习率可能不够大
  8. 极简代码:1012 数字分类 (20分)
  9. 安装好机器学习环境的虚拟机_虚拟环境之外的数据科学是弄乱机器的好方法
  10. 解决问题:Couldn't open file: data/coco.names
  11. flask get 参数_Python web 用它5分钟以后,我放弃用了四年的 Flask
  12. 参考信息 - 云原生(Cloud Native)
  13. 用几个最简单的例子带你入门 Python 爬虫
  14. BZOJ2815:[ZJOI2012]灾难(拓扑排序,LCA)
  15. [javaSE] 集合框架(迭代器)
  16. js获取页面 窗口的宽高
  17. ruby写的BT种子解析器
  18. Service Started!!!-end In Service while
  19. Python学习笔记17:玩转千图成像
  20. android手机电视下载软件安装失败,新买的电视无法安装第三方软件?方法汇总来了,解决99%的问题...

热门文章

  1. STM32 Systick定时器在实现1us延时时的问题与解决
  2. 高德地图 js自动定位到当前城市
  3. openflow 1.0中交换机对OFPT_QUEUE_GET_CONFIG_REQUEST消息的响应
  4. 论如何做好IE和Chrome互殴时的一条好池鱼之事件绑定篇
  5. 二维码的制作之根据Excel数据批量制作二维码
  6. [渝粤教育] 信阳农林学院 鱼类学 参考 资料
  7. android小程序题目,我要出题app-我要出题小程序预约 _5577安卓网
  8. 数字图像处理(1): 数字图像处理领域应用——电磁波谱 可见光
  9. 大咖云集 共享国际开源盛宴「第十三届开源中国开源世界高峰论坛」在京成功举办...
  10. mro python_Python进阶-继承中的MRO与super