一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java CAS基本實現原理代碼實例解析

Java CAS基本實現原理代碼實例解析

2020-07-31 14:56與李 Java教程

這篇文章主要介紹了Java CAS基本實現原理代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

一、前言

了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的簡稱。它有核心就是CAS與AQS。CAS是java.util.concurrent.atomic包的基礎,如AtomicInteger、AtomicBoolean、AtomicLong等等類都是基于CAS。

什么是CAS呢?全稱Compare And Swap,比較并交換。CAS有三個操作數,內存值V,舊的預期值E,要修改的新值N。當且僅當預期值E和內存值V相同時,將內存值V修改為N,否則什么都不做。

Java CAS基本實現原理代碼實例解析

二、實例

如果我們需要對一個數進行加法操作,應該怎樣去實現呢?我們模擬多個線程情況下進行操作。

ThreadDemo.java 實現一個Runnable接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.spring.security.test;
 
public class ThreadDemo implements Runnable {
 
    private int count = 0;
 
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            addCount();
        }
    }
 
    private void addCount() {
        count++;
    }
 
    public int getCount() {
        return count;
    }
}

ThreadTest.java 創建線程池,提交10個線程執行,預期結果應該是1000

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.spring.security.test;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class ThreadTest {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        ThreadDemo threadDemo = new ThreadDemo();
        for (int i = 0; i < 10; i++) {
            threadPool.submit(threadDemo);
        }
    threadPool.shutdown();
        System.out.println(threadDemo.getCount());
    }
}

運行結果:874 或其他,與預期結果不符合。

執行出來的結果并不是想象中的結果。這是為什么呢?這跟線程的執行過程有關。

Java CAS基本實現原理代碼實例解析

所以我們需要在改變count,將值從高速緩沖區刷新到主內存后,讓其他線程重新讀取主內存中的值到自己的工作內存。

此時可以用volatile關鍵字。它的作用是保證對象在內存中的可見性。

修改ThreadDemo中的count字段

private volatile int count = 0;

此時執行結果:900 或其他,與預期結果不符合。

此時還是并未得出正確執行結果。為什么?聽我細細道來。

線程安全主要體現在三個方面:

  • 原子性:提供了互斥訪問,同一時刻只能有一個線程對它進行操作
  • 可見性:一個線程對主內存的修改可以及時的被其他線程觀察到
  • 有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序的存在,該觀察結果一般雜亂無序

目前可見性已經實現了,缺少原子性的操作,因為同一時刻,多個線程對其操作,會將改動后的最新值讀取到自己的工作內存進行操作,最終只能得到后一個執行線程操作的結果,所以相當于少了一步操作,就會造成數據的不一致。

此時可以使用JUC的Atomic包下面的類來進行操作。

Java CAS基本實現原理代碼實例解析

Atomic類是使用CAS+volatile來實現原子性與可見性的。

我們來改造一下TheadDemo.java中的實現方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.spring.security.test;
 
import java.util.concurrent.atomic.AtomicInteger;
 
public class ThreadDemo implements Runnable {
 
    private AtomicInteger count = new AtomicInteger(0);
 
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            // 遞增
            count.getAndIncrement();
        }
    }
 
    public int getCount() {
        return count.get();
    }
}

執行結果: 1000,符合預期值。

Java CAS基本實現原理代碼實例解析

接下來我們來分析一下AtomicInteger類的源碼:

?
1
2
3
4
5
6
7
8
9
10
11
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
 
static {
  try {
    valueOffset = unsafe.objectFieldOffset
      (AtomicInteger.class.getDeclaredField("value"));
  } catch (Exception ex) { throw new Error(ex); }
}
 
private volatile int value;

Unsafe類是不安全的類,它提供了一些底層的方法,我們是不能使用這個類的。AtomicInteger的值保存在value中,而valueOffset是value在內存中的偏移量,利用靜態代碼塊使其類一加載的時候就賦值。value值使用volatile,保證其可見性。

?
1
2
3
4
5
6
7
8
/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
  return unsafe.getAndAddInt(this, valueOffset, 1);
}
?
1
2
3
4
5
6
7
8
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
 
    return var5;
}

var1表示當前對象,var2表示value在內存中的偏移量,var4為增加的值。var5為調用底層方法獲取value的值

compareAndSwapInt方法通過var1和var2獲取當前內存中的value值,并與var5進行比對,如果一致,就將var5+var4的值賦給value,并返回true,否則返回false

由do while語句可知,如果這次沒有設置進去值,就重復執行此過程。這一過程稱為自旋。

compareAndSwapInt是JNI(Java Native Interface)提供的方法,可以是其他語言寫的。

三、與synchronized比較

使用synchronized進行加法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.spring.security.test;
 
public class ThreadDemo implements Runnable {
 
    private int count = 0;
 
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            // 遞增
            synchronized (ThreadDemo.class) {
                count++;
            }
        }
    }
 
    public int getCount() {
        return count;
    }
}

運行結果: 1000,符合預期值。

444

使用synchronized和AtomicInteger都能得到預期結果,但是他們之間各有什么劣勢呢?

synchronized是重量級鎖,是悲觀鎖,就是無論你線程之間發不發生競爭關系,它都認為會發生競爭,從而每次執行都會加鎖。

在并發量大的情況下,如果鎖的時間較長,那將會嚴重影響系統性能。

CAS操作中我們可以看到getAndAddInt方法的自旋操作,如果長時間自旋,那么肯定會對系統造成壓力。而且如果value值從A->B->A,那么CAS就會認為這個值沒有被操作過,這個稱為CAS操作的"ABA"問題。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.cnblogs.com/yl-space/p/13390516.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 农夫69小说恋老妇小说 | 2021最新国产成人精品视频 | 日韩人成免费网站大片 | 毛片免费在线视频 | 精品国产原创在线观看视频 | 激情亚洲| 亚洲精品在线免费看 | 亚洲国产情侣偷自在线二页 | 岛国虐乳紧缚媚药调教 | 免费国产一级观看完整版 | 美女隐私部位视频网站 | 久久久久国产一级毛片高清片 | 22sihu国产精品视频影视资讯 | 国产成人影院一区二区 | 国产成人精品一区 | 日韩精品高清自在线 | 好大好深视频 | 国产 国语对白 露脸正在播放 | 日剧整部剧护妻狂魔免费观看全集 | 国产精品视频免费观看 | 三极黄色 | 国产在线成人a | 亚洲黄色色图 | 成人深夜视频 | 国产自一区 | 精品国产福利在线观看一区 | 果冻传媒在线播放观看228集 | 日本在线一区二区 | 日韩在线成人 | 亚洲精品人成网在线播放影院 | 国产精品久久亚洲一区二区 | 福利一区二区在线观看 | 短篇小说肉| 国产99精品 | 无限资源在线观看高清 | 精品国产一区二区三区国产馆 | 污斗罗大陆 | 消息称老熟妇乱视频一区二区 | 亚洲美女人黄网成人女 | 波多野结衣小说 | 亚洲一区二区三区在线播放 |