Java并发编程实战 第15章 原子变量和非阻塞同步机制
非阻塞的同步机制
简单的说,那就是又要实现同步,又不使用锁。
与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势。
实现非阻塞算法的常见方法就是使用volatile语义和原子变量。
硬件对并发的支持
原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令。
模拟实现AtomicInteger的++操作
首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的。不需要加锁。
- public synchronized int compareAndSwap(int exceptValue, int newValue){
- int oldValue = count;
- if(oldValue == exceptValue){
- count = newValue;
- }
- return oldValue;
- }
注意:CAS总是返回oldValue。
使用上面的方法模拟AtomicInteger的++操作。
- class MyAtomicInteger{
- private int value;
- public int incrementAndGet()
- {
- int v ;
- do{
- v = value;
- }while(v != compareAndSwap(v,v+1));
- return v + 1;
- }
- }
注意:Java的AtomicInteger大概实现机制就是这样的,不会阻塞,使用处理器的CAS功能,但是要轮询尝试。
看起来轮询尝试性能会更差,其实不然,当竞争不是非常高的时候,基于CAS的算法更胜一筹。
原子类
AtomicBoolean AtomicInteger AtomicLong AtomicReference
原子数组类:AtomicIntegerArray AtomicLong AtomicReferenceArray。
使用volatile语法修饰的数组只能保证数组变量本身的volatile语义,不能保证元素的volatile语义。这个时候应该使用,原子数组类。
注意:AtomicBoolean AtomicInteger AtomicLong和非原子的对应数值类如Integer截然不用。实现机制完全不一样,也没有对应关系。最重要的一个差别:这个三个原子类是可变的。而且是使用的Object的hashCode和equals方法,没有自己扩展。
性能比较:锁与原子变量
在中低程度的竞争下,原子变量能提供很高的可伸缩性,原子变量性能超过锁;而在高强度的竞争下,锁能够更有效地避免竞争,锁的性能将超过原子变量的性能。但在更真实的实际情况(一般没有那么高强大的竞争)中,原子变量的性能将超过锁的性能。
注意:不论是锁还是原子变量,都远远比不上避免共享状态(如使用线程封闭技术,但是使用场景计较局限),彻底消除竞争的效率。
两个非阻塞的算法示例
- /**
- * 使用Treiber算法构造的非阻塞栈
- */
- public class ConcurrentStack<E> {
- private AtomicReference<Node<E>> top = new AtomicReference<ConcurrentStack.Node<E>>();
- public void push(E item){
- Node<E> newHead = new Node<E>(item);
- Node<E> oldHead;
- do{
- oldHead = top.get();
- newHead.next = oldHead;
- } while (!top.compareAndSet(oldHead, newHead));
- }
- public E pop(){
- Node<E> oldHead;
- Node<E> newHead;
- do {
- oldHead = top.get();
- if (oldHead == null)
- return null;
- newHead = oldHead.next;
- } while (!top.compareAndSet(oldHead, newHead));
- return oldHead.item;
- }
- private static class Node<E>{
- public final E item;
- public Node<E> next;
- public Node(E item){
- this.item = item;
- }
- }
- }
下面这个没有看懂,留在这里记录,以后再看:
- /**
- * 链表中非阻塞算法中的插入排序,来自Michael-Scott
- */
- public class LinkedQueue<E> {
- private static class Node<E>{
- final E item;
- final AtomicReference<Node<E>> next;
- public Node(E item, Node<E> next){
- this.item = item;
- this.next = new AtomicReference<>(next);
- }
- }
- private final Node<E> dummy = new Node<E>(null, null);
- private final AtomicReference<Node<E>> head =
- new AtomicReference<>(dummy);
- private final AtomicReference<Node<E>> tail =
- new AtomicReference<>(dummy);
- public boolean put(E item){
- Node<E> newNode = new Node<E>(item, null);
- while (true){
- Node<E> curTail = tail.get();
- Node<E> tailNext = curTail.next.get();
- if (curTail == tail.get()){ //尾部还未修改
- if (tailNext != null){
- // 队列处于中间状态(即新节点已经接上,尾节点还未更新),推进尾节点
- tail.compareAndSet(curTail, tailNext);
- } else{
- // 处于稳定状态, 尝试插入新节点
- if (curTail.next.compareAndSet(null, newNode)){
- // 插入成功后,推进尾节点
- tail.compareAndSet(curTail, tailNext);
- return true;
- }
- }
- }
- }
- }
- }
Java并发编程实战 第15章 原子变量和非阻塞同步机制的更多相关文章
- 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记
一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...
- Java多线程并发编程之原子变量与非阻塞同步机制
1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞 ...
- Java并发编程实战---第六章:任务执行
废话开篇 今天开始学习Java并发编程实战,很多大牛都推荐,所以为了能在并发编程的道路上留下点书本上的知识,所以也就有了这篇博文.今天主要学习的是任务执行章节,主要讲了任务执行定义.Executor. ...
- Java并发编程实战 第16章 Java内存模型
什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...
- java并发编程实战:第十五章----原子变量与非阻塞机制
非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃 ...
- 【java并发编程实战】第一章笔记
1.线程安全的定义 当多个线程访问某个类时,不管允许环境采用何种调度方式或者这些线程如何交替执行,这个类都能表现出正确的行为 如果一个类既不包含任何域,也不包含任何对其他类中域的引用.则它一定是无状态 ...
- java并发编程实战:第二章----线程安全性
一个对象是否需要是线程安全的取决于它是否被多个线程访问. 当多个线程访问同一个可变状态量时如果没有使用正确的同步规则,就有可能出错.解决办法: 不在线程之间共享该变量 将状态变量修改为不可变的 在访问 ...
- Java并发编程实战 第8章 线程池的使用
合理的控制线程池的大小: 下面内容来自网络.不过跟作者说的一致.不想自己敲了.留个记录. 要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析: 任务的性质:CPU密集型任务.IO ...
- 《Java并发编程实战》第二章 线程安全性 读书笔记
一.什么是线程安全性 编写线程安全的代码 核心在于要对状态訪问操作进行管理. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与 ...
随机推荐
- 三十七:数据库之SQLAlchemy外建之多对多关系
准备工作 from sqlalchemy import create_engine, Column, Integer, String, Float, Text, ForeignKeyfrom sqla ...
- git clone 指定分支操作
服务器迁移,而且原来本地开发是在同一个目录中切换不同的分支,感觉有点挫,于是打算一个文件目录对应一个分支,这样不会有太大的文件差异. 记录下来本次操作,可能以后还会用到. git初始化一般是这样. g ...
- LeetCode.949-给定数字的最大时间(Largest Time for Given Digits)
这是悦乐书的第363次更新,第391篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第225题(顺位题号是949).给定4个整数组成的数组,返回最大的24小时时间. 最小的 ...
- 手把手带你发布Nuget包-图文说话
博客:https://www.cnblogs.com/24klr/
- mysql的最左索引匹配原则
最近复习数据库,主要看的是mysql.很多东西忘得一干二净.看到某乎上有个答案非常给力,就记录一下,以后方便查看. 链接:https://www.zhihu.com/question/36996520 ...
- poj1011(DFS+剪枝)
题目链接:https://vjudge.net/problem/POJ-1011 题意:给定n(<=64)条木棍的长度(<=50),将这些木棍刚好拼成长度一样的若干条木棍,求拼出的可能的最 ...
- Connection is read-only. Queries leading to data modification are not allowed 错误原因
因为我再spring 中使用了AOP进行事务管理,有如下配置 <tx:advice id="txAdvice" transaction-manager="trans ...
- BZOJ 4857 反质数序列
题面 奇数+奇数一定不是质数(1+1除外),偶数+偶数一定不是质数,质数只可能出现在偶数+奇数中 把所有的点排成两列,权值为奇数的点在左边,权值为偶数的在右边 如果左边的点x+右边的点y是质数,我们就 ...
- C++中函数模板的概念和意义
1,对泛型编程进行学习,泛型编程是实际工程开发中必用的技术,大型公司的通用 库都是采用泛型编程的技术完成的,C++ 中支持泛型编程技术,C++ 中的函数 模板和类模板就是 C++ 中泛型编程技术,本 ...
- 邀请好友注册页面光标点到输入框后,输入框会先灰一下。只有ios存在
输入框会先灰一下.只有ios存在 用这一行代码可以解决问题: -webkit-tap-highlight-color:transparent;