原子操作

并发编程的基石——CAS机制

爷,独闯天下 提交于 2020-01-07 11:29:35
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。 并发编程系列博客传送门 Java中提供了很多原子操作类来保证共享变量操作的原子性。这些原子操作的底层原理都是使用了CAS机制。在使用一门技术之前,了解这个技术的底层原理是非常重要的,所以本篇博客就先来讲讲什么是CAS机制,CAS机制存在的一些问题以及在Java中怎么使用CAS机制。 其实Java并发框架的基石一共有两块,一块是本文介绍的CAS,另一块就是AQS,后续也会写博客介绍。 什么是CAS机制 CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前,我们先来聊下在多线程环境下,对共享变量进行数据更新的两种模式:悲观锁模式和乐观锁模式。 悲观锁更新的方式认为:在更新数据的时候大概率会有其他线程去争夺共享资源,所以悲观锁的做法是:第一个获取资源的线程会将资源锁定起来,其他没争夺到资源的线程只能进入阻塞队列,等第一个获取资源的线程释放锁之后,这些线程才能有机会重新争夺资源。synchronized就是java中悲观锁的典型实现,synchronized使用起来非常简单方便,但是会使没争抢到资源的线程进入阻塞状态,线程在阻塞状态和Runnable状态之间切换效率较低(比较慢)。比如你的更新操作其实是非常快的

Java并发编程---原子操作CAS

≯℡__Kan透↙ 提交于 2020-01-07 01:25:33
实现并发操作的方法有两种:一种是使用锁(Synchronized和Lock),另外一种是使用原子操作(CAS) Synchronized基于阻塞的锁机制可能会带来的问题: a. 被阻塞的线程优先级很高 b.拿到锁的线程一直不释放锁怎么办? c.大量的竞争消耗cpu,同时带来死锁或者其他安全问题 基于上述问题,提出了CAS原子操作 a.CAS原理:利用现代处理器都支持的CAS指令,循环这个指令,直到成功为止 b.CAS(Compare And Swap) : 指令级别上保证这是一个原子操作 c.与CAS相关的三个运算符:一个内存地址V,一个期望值A,一个新值B 基本思路:如果地址V上的值和期望的值A相等,就给地址V赋新值B,如果不是,不做任何操作。 (在(死循环)循环中不断进行CAS操作) d.CAS也会带来一定的问题: 1)ABA问题(A–>B–>A):当第一次取内存地址V时,取到了A的值,可当它要执行CAS操作之前,已经有一个线程对A的值改为B后,再 改为A,这时候的值虽然一样,不过内容已经有过更改。(举例:在现实生活中,你放在座子上的以一杯水被同事喝完,又重新倒满一杯放在那里。很明显,它虽然还是那杯满着的水,可性质上却完全不一样了。) 解决方法:可以通过版本号解决 A1—>B2---->A3 (类似于乐观锁,大多是基于数据版本( Version )记录机制实现。) 2)开销问题

Java 理论与实践: 正确使用 Volatile 变量--转

耗尽温柔 提交于 2020-01-05 04:07:19
原文地址:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized ”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形。 锁提供了两种主要特性: 互斥(mutual exclusion) 和 可见性(visibility) 。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。 Volatile 变量 Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例

Java多线程编程(3)--线程安全性

核能气质少年 提交于 2020-01-05 03:31:10
一.线程安全性   一般而言,如果一个类在单线程环境下能够运作正常,并且在多线程环境下,在其使用方不必为其做任何改变的情况下也能运作正常,那么我们就称其是线程安全的。反之,如果一个类在单线程环境下运作正常而在多线程环境下则无法正常运作,那么这个类就是非线程安全的。因此, 一个类如果能够导致竞态,那么它就是非线程安全的;而一个类如果是线程安全的,那么它就不会导致竞态。下面是《Java并发编程实战》一书中给出的对于线程安全的定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。   使用一个类的时候我们必须先弄清楚这个类是否是线程安全的。因为这关系到我们如何正确使用这些类。Java标准库中的一些类如ArrayList、HashMap和SimpleDateFormat,都是非线程安全的,在多线程环境下直接使用它们可能导致一些非预期的结果,甚至是一些灾难性的结果。一般来说,Java标准库中的类在其API文档中会说明其是否是线程安全的(没有说明其是否是线程安全的,则可能是也可能不是线程安全的)。   从线程安全的定义上我们不难看出,如果一个线程安全的类在多线程环境下能够正常运作,那么它在单线程环境下也能正常运作。既然如此

并发Bug之源有三,请睁大眼睛看清它们

会有一股神秘感。 提交于 2020-01-04 05:13:31
写在前面 生活中你一定听说过——能者多劳 作为 Java 程序员,你一定听过——这个功能请求慢,能加一层缓存或优化一下 SQL 吗? 看过中国古代神话故事的也一定听过——天上一天,地上一年 一切设计来源于生活,上一章 学并发编程,透彻理解这三个核心是关键 中有讲过,作为"资本家",你要尽可能的榨取 CPU,内存与 IO 的剩余价值,但三者完成任务的速度相差很大,CPU > 内存 > IO分,CPU 是天,那内存就是地,内存是天,那 IO 就是地,那怎样平衡三者,提升整体速度呢? CPU 增加缓存,还不止一层缓存,平衡内存的慢 CPU 能者多劳,通过分时复用,平衡 IO 的速度差异 优化编译指令 上面的方式貌似解决了木桶短板问题,但同时这种解决方案也伴随着产生新的 可见性,原子性,和有序性 的问题,且看 三大问题 可见性 一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性 谈到可见性,要先引出 JMM (Java Memory Model) 概念, 即 Java 内存模型,Java 内存模型规定,将所有的变量都存放在 主内存 中,当线程使用变量时,会把主内存里面的变量 复制 到自己的工作空间或者叫作 私有内存 ,线程读写变量时操作的是自己工作内存中的变量。 用 Git 的工作流程理解上面的描述就很简单了, Git 远程仓库就是主内存,Git 本地仓库就是自己的工作内存

原子操作类AtomicInteger

为君一笑 提交于 2020-01-04 03:16:15
目录 为什么需要原子操作类 如果使用volatile修饰变量呢 使用AtomicInteger CAS指令 incrementAndGet()方法 JUC包原子类的类型及方法 为什么需要原子操作类 原子操作,是指一个操作是不可分割,不可中断的; 在java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下操作就是线程不安全的;(n++解析为n=n+1,明显这个操作不具备原子性,多线程并发共享这个变量时必然会出现问题) 如果使用volatile修饰变量呢 volatile特性 可见性,保证被修饰的变量在线程间可见,对变量的所有写操作都能立即反应到其他线程中;即volatile修饰的变量在各个线程中是一致的; 只是保证了可见性,在并发情况下并不能保证线程安全(n++不具备原子性) 使用AtomicInteger incrementAndGet()方法具备原子性 同步 ,多线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程或一些使用; 阻塞同步和非阻塞同步都是实现线程安全的两个保障手段;非阻塞同步主要解决了阻塞同步中线程阻塞和唤醒带来的性能问题; 非阻塞同步 :在并发环境下,某个线程对共享变量先进行操作;如果没有其他线程争用共享数据那操作就成功;如果存在数据争用冲突,就采取补偿措施,比如重试机制,直到成功为止;因为这中乐观的并发策略不需要把线程挂起

volatile 说明

一个人想着一个人 提交于 2020-01-04 02:51:39
volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景。以下是本文的目录大纲:一.内存模型的相关概念二.并发编程中的三个概念三.Java内存模型四..深入剖析volatile关键字五.使用volatile关键字的场景若有不正之处请多多谅解,并欢迎批评指正。请尊重作者劳动成果,转载请标明原文链接:http://www.cnblogs.com/dolphin0520/p/3920373.html一.内存模型的相关概念大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多

CAS都不了解,你还怎么看J.U.C

一笑奈何 提交于 2020-01-03 22:37:40
前言 说到 CAS (CompareAndSwap),不得不先说一说 悲观锁 和 乐观锁 ,因为CAS是乐观锁思想的一种实现。 悲观锁 :总是很悲观的认为,每次拿数据都会有其他线程并发执行,所以每次都会进行加锁,用完之后释放锁,其他的线程才能拿到锁,进而拿到资源进行操作。java中的synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 乐观锁 :总是很乐观认为,自己拿到数据操作的时候,没有其他线程来并发操作,等自己操作结束要更新数据时,判断自己对数据操作的期间有没有其他线程进行操作,如果有,则进行重试,直到操作变更成功。乐观锁常使用CAS和版本号机制来实现。java中 java.util.atomic 包下的原子类都是基于CAS实现的。 一、什么是CAS CAS指 CompareAndSwap ,顾名思义, 先比较后交换 。比较什么?交换什么呢? CAS中有三个变量:内存地址V,期待值A, 更新值B。 当且仅当内存地址V对应的值与期待值A时相等时,将内存地址V对应的值更换为B。 二、atomic包 有了悲观锁,乐观锁的知识,让我们走进java.util.atomic包,看一看java中CAS的实现。 这就是 java.util.atomic 包下的类,我们着重看AtomicInteger源码(其他的都是一样的思想实现的) 然后思考CAS有什么弊端

zeromq源码分析笔记之无锁队列ypipe_t(3)

女生的网名这么多〃 提交于 2020-01-03 02:41:04
在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令。而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以我们先来看看yqueue_t是怎么实现的。 1、yqueue_t yqueue_t是一个高效的队列,高效体现在她的内存配置上,尽量少的申请内存,尽量重用将要释放的内存。其实,容器的设计都会涉及这点--高效的内存配置器,像sgi stl容器的内存配置器,使用了内存池,预先分配一块较大内存,用不同大小的桶管理,容器申请内存时从相应的桶里拿一块内存,释放内存时又把内存回收到相应的桶里,这样就能做到尽量少的malloc调用。yqueue_t并没有使用内存池,但是利用了同样的思想,一次性分配一个chunk_t减少内存分配次数,并用spare_chunk管理将要释放的块用于内存回收,详细的实现后面再说,先看一下yqueue_t的整个概况,源码位于Yqueue.hpp // T is the type of the object in the queue.队列中元素的类型 // N is granularity(粒度) of the queue,简单来说就是yqueue_t一个结点可以装载N个T类型的元素,可以猜想yqueue_t的一个结点应该是个数组 template <typename T,

DOCK6学习(2)

假装没事ソ 提交于 2020-01-02 16:27:37
DOCK6学习II DOCK6 的工作流程 DOCK6工作流程概论 DOCK6基本理念 DOCK6相关概念 (A)Sphere Centers (B)化学匹配 (C) Critical Points (D)Bump Filter DOCK6 的工作流程 本文是咸鱼博主根据dock6.9的手册总结出,只是针对小分子-蛋白相互作用和Amber Score。感觉也不会有人看,但又怕后面没人接暂时写下来的了233。(实际上就是个搬运翻译而已和自己的操作而已啦。) DOCK6工作流程概论 DOCK6.9的工作流程如图。首先是根据`受体(Receptor)和配体(ligand)的几何坐标来进行准备,而后进行docking。受体的准备有三个流程: Sphgen , Grid 和 DOCK 。 Sphgen 流程识别相应位点,并生成可填满位点的球心。 Grid 流程则生成记分网格。在程序 DOCK 中, DOCK 将球体(由 Sphgen 生成)与配体原子相匹配,并使用记分网格(来自 Grid )来评估配体的取向。程序对接还可以最小化基于能量的分数。 DOCK6基本理念 针对docking,DOCK的处理方式是: (1)确定配体相对蛋白的方位, (2)确定给配体方位的打分方式. DOCK这个方式中的两种处理方式都可以进行替换。替换成自己需要的方式来进行 确定配体的方位的步骤: (1