原子

深入CAS的底层实现机制,以及对应的使用风险

独自空忆成欢 提交于 2019-12-04 14:18:50
概述 CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术,Java并发包中的很多类都使用了CAS技术。CAS也是现在面试经常问的问题,本文将深入的介绍CAS的原理。 案例 介绍CAS之前,我们先来看一个例子。 /** * @author joonwhee * @date 2019/7/6 */ public class VolatileTest { public static volatile int race = 0; private static final int THREADS_COUNT = 20; public static void increase() { race++; } public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[THREADS_COUNT]; for (int i = 0; i < THREADS_COUNT; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) { increase(); } } })

JDK中原子性的支持

纵饮孤独 提交于 2019-12-04 13:24:18
在Java开发中,假设对共享变量除了赋值之外并不完成其他操作, 那么可以将这些共享变量声明为 volatile 或者final。 然而经常我们除了对共享变量赋值之外,还需要对共享变量进行修改操作,比如自增,自减等系列操作,那么此时就需要保证共享变量的 原子性 ,所谓原子性,就是站在机器的角度,一个不可分隔的执行单元。在JDK中,java.util.concurrent.atomic 包中有很多类使用了很高效的机器级指令(而不是使用 锁) 来保证其他操作的原子性。 比如说AtomicLong类,提供了方法 incrementAndGet 和 decrementAndGet, 它们分别以原子方式将一个整数自增或自减。例如, 可以安全地生成一个 数值序列,像这样: public static AtomicLong atomicLong = new AtomicLong(1); public static void main(String[] args) { long res = atomicLong.incrementAndGet(); System.out.println(res); } incrementAndGet 方法以原子方式将 AtomicLong 自增, 并返回自增后的值。 也就是说, 获得值、 增 1 并设置然后生成新值的操作不会中断。

Volatile详解

匿名 (未验证) 提交于 2019-12-03 00:21:02
之前研究了一下内存模型,java内存模型中很关键的一点就是Volatile,现在跟大家探讨一下Volatile的知识吧 1.volatile关键字的两层语义   一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:   1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。   2)禁止进行指令重排序。   先看一段代码,假如线程1先执行,线程2后执行: 这段代码是很典型的一段代码,很多人在中断线程时可能都会采用这种标记办法。但是事实上,这段代码会完全运行正确么?即一定会将线程中断么?不一定,也许在大多数时候,这个代码能够把线程中断,但是也有可能会导致无法中断线程(虽然这个可能性很小,但是只要一旦发生这种情况就会造成死循环了)。   下面解释一下这段代码为何有可能导致无法中断线程。在前面已经解释过,每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。   那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。   但是用volatile修饰之后就变得不一样了:   第一

【c++】atomic原子操作基本用法,模板函数。

匿名 (未验证) 提交于 2019-12-03 00:02:01
【转载】: https://owent.net/2012/611.html 主要的函数如下: 函数名 | 描述 | ―――――|――――-| atomic_store | 保存非原子数据到原子数据结构 | atomic_load | 读取原子结构中的数据 | atomic_exchange | 保存非原子数据到原子数据结构,返回原来保存的数据 | atomic_fetch_add | 对原子结构中的数据做加操作 | atomic_fetch_sub/atomic_fetch_sub_explicit | 对原子结构中的数据做减操作 | atomic_fetch_and | 对原子结构中的数据逻辑与 | atomic_fetch_or | 对原子结构中的数据逻辑或 | atomic_fetch_xor | 对原子结构中的数据逻辑异或 刚才提到了在原子操作时候的内存操作规则,内存操作规则主要是 std::memory_order,这是个枚举类型,里面包含着N多规则 ֵ | 定义规则 | memory_order_relaxed | 不保证顺序 | memory_order_consume | 类比生产者-消费者模型中的消费者读取动作(仅是读取,无计数器),保证该操作先于依赖于当前读取的数据(比如后面用到了这次读取的数据)不会被提前,但不保证其他读取操 作的顺序

Java原子类实现原理分析

匿名 (未验证) 提交于 2019-12-02 21:53:52
并发包中的原子类可以解决类似num++这样的复合类操作的原子性问题,相比锁机制,使用原子类更精巧轻量,性能开销更小,下面就一起来分析下原子类的实现机理。 悲观的解决方案(阻塞同步)   我们知道,num++看似简单的一个操作,实际上是由 1.读取 2.加一 3.写入 所以我们之前提到过的volatile是无法解决num++的原子性问题的 ),在并发环境下,如果不做任何同步处理,就会有线程安全问题。最直接的处理方式就是 加锁 。 synchronized(this){ num++; }   使用独占锁机制来解决,是一种 悲观的 并发策略,抱着一副“总有刁民想害朕”的态势,每次操作数据的时候都认为别的线程会参与竞争修改,所以直接加锁。同一刻只能有一个线程持有锁,那其他线程就会阻塞。线程的挂起恢复会带来很大的性能开销,尽管jvm对于非竞争性的锁的获取和释放做了很多优化, 但是一旦有多个线程竞争锁,频繁的阻塞唤醒,还是会有很大的性能开销的 。所以,使用synchronized或其他重量级锁来处理显然不够合理。 乐观的解决方案(非阻塞同步)   乐观的解决方案,顾名思义,就是很大度乐观,每次操作数据的时候,都认为别的线程不会参与竞争修改,也不加锁。如果操作成功了那最好;如果失败了,比如中途确有别的线程进入并修改了数据(依赖于冲突检测),也不会阻塞,可以采取一些补偿机制,一般的策略就是反复重试

深入理解Atomic原子类

匿名 (未验证) 提交于 2019-12-02 21:40:25
Atomic是基于 unsafe类 和 自旋操作 实现的,下面以AtomicInteger类为例进行讲解。 要理解Atomic得先了解CAS CAS CAS全程Compare And Swap ,是条并发原语,功能是判断内存中某个值是否与预期值相等,相等就用新值更新旧值,否则不更新。 Java中CAS是基于unsafe类实现的,所有的unsafe类中的方法都是native类修饰的,直接调用操作系统底层资源执行响应的任务。 unsafe.compareAndSwapInt(this, valueOffset, expect, update); 这是一条调用unsafe类中的compareAndSwapInt的方法,this表示当前对象,valueoffset表示当前对象的偏移地址,expect表示预期值 update表示更新值。作用是如果预期值和该对象偏移地址中的值一样,就用更新值更新偏移地址中的值。 AtomicInteger类初始化 // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset

线程同步 ——volatile

一个人想着一个人 提交于 2019-12-02 18:52:13
上一篇我们简单的使用了synchoronized实现线程同步,接下来简单的使用volatile实现线程同步。 public class ThreadCount { //如果去掉volatile就会出现错误 private static volatile int total; public static void main(String[] args) { Counter counter1 = new Counter(10); Counter counter2 = new Counter(20); Counter counter3 = new Counter(5); Counter counter4 = new Counter(10); Counter counter5 = new Counter(10); Counter counter6 = new Counter(20); counter1.start(); counter2.start(); counter3.start(); counter4.start(); counter5.start(); counter6.start(); } static class Counter extends Thread { private int i; Counter(int i) { this.i = i; } @Override

ES8新特性——SharedArrayBuffer对象、Atomics对象

本秂侑毒 提交于 2019-12-02 06:40:30
SharedArrayBuffer对象 SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。 语法 new SharedArrayBuffer(length) 参数length指所创建的数组缓冲区的大小,以字节(byte)为单位。 需要new运算符构造 // 创建一个1024字节的缓冲 let sab = new SharedArrayBuffer ( 1024 ) ; MDN文档 Atomics对象 Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。 这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。 多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。 注意