原子变量和CAS算法以及ConcurrentHashMap

1.首先做一个测试:i++。输出结果为10,因为在底层实现的时候会引入一个临时变量具体为:

 1 public static void main(String[] args) {
 2         //i++测试:
 3         int i=10;
 4         i=i++;
 5         System.out.println(i);
 6         /*
 7         *底层原理:
 8         _temp = i ;
 9         i = i + 1 ;
10         i = _temp ;
11         * */
12     }

所以i++就是一个非原子性操作,采用多线程再次测试:测试结果中会因为i++的非原子性操作带来读写不一致问题。比如:1,3,2,2,4,5,6,7,8,9序列的出现。两个2的出现就说明了,两个线程在同时从主存中读取,和进行加一操作时出现的不一致问题,那么可以用Volatile来解决这个不一致问题吗?不行。因为,这是一个非原子性操作。

 1 public class TestAtomicDemo {
 2     public static void main(String[] args) {
 3
 4         Testi testi = new Testi();
 5
 6         for (int i = 0; i < 10; i++) {
 7             new Thread(testi, String.valueOf(i)).start();
 8         }
 9     }
10 }
11 class Testi implements Runnable{
12     int a=1;
13     @Override
14     public void run() {
15         try {
16             Thread.sleep(1000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         System.out.println(Thread.currentThread().getName()+": "+getA());
21     }
22     public int getA() {
23         return a++;
24     }
25 }

2.Java并发包(java.util.concurrent)的主要实现机制:

采用Volatile保证内存的可见性;

采用CAS(Compare And Swap)算法保证数据的原子性。

3.乐观锁(CAS算法)和悲观锁(synchronized):

Synchronized就是一种悲观锁,因为它总是假设最坏的情况,每次去拿数据都会认为别人会修该,所以每次都会加锁,效率较低。

乐观锁其实是一种思想,它具体有两个步骤:冲突检测和数据更新。所以它不会每次访问都进行加锁控制,只是在进行更新操作时才进行冲突检测和数据更新,适合用于读操作较多的程序,因此大大提高了吞吐量。其实现方式比较典型的就是CAS算法。当多个线程同时更新同一个变量时,只会有一个线程成功修改,其他的都会失败,更新失败的线程不会被挂起,而是被告知在这次竞争中失败了,并可以再次尝试。

4.CAS(Compare And Swap)原理: 

首先涉及三个操作数:

内存值(V)--->首次读取内存中的值;

预估值(A)--->再一次读取的内存中的值;

更新值(B)--->新值;

每次在进行更新操作时,当且仅当V==A(内存值和预期值相等时),后 V=B(将内存值更新为B),否则不进行任何操作。

CAS是一种非阻塞算法的常见实现,JUC性能因此大大提升。

 1 //模拟CAS算法:
 2 public class TestCAS {
 3     public static void main(String[] args) {
 4         Cas cas = new Cas(10);
 5         for (int i = 0; i < 10; i++) {
 6             new Thread(() -> {
 7                 //10个线程都进行对数据的更新操作
 8                 int expectedValue = cas.getValue();  //更新前获得预期值
 9                 boolean b = cas.CompareTest(expectedValue, (int) Math.random());
10                 System.out.println(b);
11
12             }).start();
13         }
14     }
15 }
16 class Cas{
17     /*
18     * 内存值;预期值;新值。
19     * */
20
21     int value;  //内存值
22
23     public synchronized int getValue() {
24         return value;
25     }
26
27     public Cas(int value) {   //构造方法
28         this.value = value;
29     }
30
31     //比较交换,并将内存值返回
32     public int CompareSwap(int expectedValue,int newValue){
33         int oldValue=value;  //获得内存值
34         if(oldValue==expectedValue){
35             value=newValue;
36         }
37         return oldValue;
38     }
39
40     //检测是否更新成功
41     public boolean CompareTest(int expectedValue,int newValue){
42         boolean flag=false;
43         if(expectedValue==CompareSwap(expectedValue,newValue)){
44             //判断预期值和内存值是否相等->更新是否成功
45             flag=true;
46         }
47         return flag;
48     }
49 }

CAS算法的ABA问题(当前线程的CAS操作无法分辨,值是否已经发生过变化):解决方法->在修改值的同时增加一个时间戳,只有当预期值和时间戳都相同时才进行修改。



原文地址:https://www.cnblogs.com/xbfchder/p/11040550.html

时间: 2024-10-29 16:03:47

原子变量和CAS算法以及ConcurrentHashMap的相关文章

原子变量与CAS算法

上一节讨论了 volatile关键字,volatile关键字修饰的作用是不具有 "原子性" 和 "互斥性的" 例如 i++ 操作 就不是一个原子性的操作,i++ 其实分为3个步骤进行 "读-改-写" int temp = i; i = i + 1; i= temp; 先看一段代码: package com.java.juc; public class TestAtomicDemo { public static void main(String[

三、原子变量与CAS算法

原子变量:jdk1.5 后 java.util.concurrent.atomic 包下提供了常用的原子变量: - AtomicBoolean - AtomicInteger - AtomicLong - AtomicReference - AtomicIntegerArray - AtomicLongArray - AtomicMarkableReference - AtomicReferenceArray - AtomicStampedReference 1.以上类中的变量都是volatil

volatile关键字与内存可见性&amp;原子变量与CAS算法

1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 3 .模拟CAS算法 TestVolatile package com.aff.juc; /* 1.volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 相较于synchronized是一种较为轻量级的同步策略 注意: volatile不具备"互斥性" 不能保

用Java原子变量的CAS方法实现一个自旋锁

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/5999610.html 实现: package sjq.mylock; import java.util.concurrent.atomic.AtomicReference; public class SpinLock { private AtomicReference<Thread> owner = new

个人笔记--内存可见性和原子变量

jdk1.6以后提供了java并发包. volatile与内存可见性: 例子: 结果: 结论: main()线程读取到的td.isFlag并不是true. 这就涉及到了内存可见性问题. 具体原因: 重排序:代码书写的顺序与实际执行的顺序不同. 1.  编译器重排序 2.  指令重排序 3.  内存系统重排序 As-if-serial: 无论如何重排序,程序执行的记过应该与代码顺序执行的结果一致.Java编译器和处理器在运行时都会保证在单线程下遵循这个语言. 补充:jstack可以生成线程快照(j

2.原子变量 CAS算法

前面提到,使用volatile无法保证 变量状态的原子性操作,所谓原子性,就是不可再分 如:i++的原子性问题,i++ 的操作实际上分为三个步骤  "读-改-写" (1)保存i的值(一个临时变量中) (2)递增i (3)返回已保存的值 当在并发的条件下执行 i++, 线程1执行 i++,先从主存中 获取 i 的 值(假设初值i=0),还未等 执行i = i + 1,此时线程2进来,也从主存中获取 i 的 值(0) 然后 线程1 执行了 i = i + 1:(i=1) 线程2再执行 i

Java并发——原子变量和原子操作与阻塞算法

十五年前,多处理器系统是高度专用系统,要花费数十万美元(大多数具有两个到四个处理器).现在,多处理器系统很便宜,而且数量很多,几乎每个主要微处理器都内置了多处理支持,其中许多系统支持数十个或数百个处理器. 要使用多处理器系统的功能,通常需要使用多线程构造应用程序.但是正如任何编写并发应用程序的人可以告诉你的那样,要获得好的硬件利用率,只是简单地在多个线程中分割工作是不够的,还必须确保线程确实大部分时间都在工作,而不是在等待更多的工作,或等待锁定共享数据结构. 问题:线程之间的协调 如果线程之间 

并发策略-CAS算法

对于并发控制而言,我们平时用的锁(synchronized,Lock)是一种悲观的策略.它总是假设每一次临界区操作会产生冲突,因此,必须对每次操作都小心翼翼.如果多个线程同时访问临界区资源,就宁可牺牲性能让线程进行等待,所以锁会阻塞线程执行. 与之相对的有一种乐观的策略,它会假设对资源的访问是没有冲突的.既然没有冲突也就无需等待了,所有的线程都在不停顿的状态下持续执行.那如果遇到问题了无锁的策略使用一种叫做比较交换(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就

Java并发编程实战 第15章 原子变量和非阻塞同步机制

非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatile语义和原子变量. 硬件对并发的支持 原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令. 模拟实现AtomicInteger的++操作 首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的.不需要加锁. public s