日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

1. AtomicInteger 、Unsafe 及 CAS方法的整理

發布時間:2023/10/11 综合教程 67 老码农
生活随笔 收集整理的這篇文章主要介紹了 1. AtomicInteger 、Unsafe 及 CAS方法的整理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文摘自:  

  https://blog.csdn.net/fanrenxiang/article/details/80623884

  http://ifeve.com/sun-misc-unsafe/

  https://blog.csdn.net/likailonghaha/article/details/70156858

  

一、前言

 1、為什么需要AtomicInteger原子操作類?

  對于全局變量的數值類型作 count++,若沒有加 synchronized 關鍵字則是線程不安全的,count++ 解析為 :

  ①、讀取 count 值 ②、賦值 count = count + 1;顯然,這個操作不具備原子性,多線程時會出現問題,如下測試:

     public static int count = 0;

     public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
new Thread() {
public void run() {
count++;
}
}.start();
}
System.out.println("count: " + count);
}

輸出的結果為count: 94,這個值不定,每次測試都可能不一樣,很顯然,100個線程跑++操作,結果并沒有像預期的那樣count: 100。

  2、要是換成volatile修飾count變量呢?

  volatile 修飾的變量能夠在線程之間保持可見性,能被多個線程同時讀但是又能保證只被單線程寫,而且不會讀到過期值,volatile 修飾字段的寫入操作總是優先于讀操作,即使多個線程同時修改 volatile 變量字段,總能保證獲取到的是最新的值。如下測試:

     public  static  volatile  int count = 0;

     public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 100; j++) {
count++;
}
}
}.start();
}
Thread.sleep(1000);
System.out.println("volatile count: " + count); }

結果似乎又失望了,出現volatile count: 9992,果然還是出現問題了,volatile僅僅保證變量在線程間保持可見性,卻依然不能保證非原子性的操作。

  3、用了AtomicInteger類后會變成什么樣子呢?

public class AtomicIntegerTest2 {

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 100; j++) {
count.getAndIncrement();
}
}
}.start();
}
Thread.sleep(1000);
System.out.println("AtomicInteger count: " + count);
}
}

結果每次都輸出"AtomicInteger count: 10000",沒毛病。

二、AtomicInteger、Unsafe 類介紹

  1、AtomicInteger 

    來自 java.util.concurrent.atomic 包,atomic包下提供AtomicBoolean/AtomicLong/AtomicInteger三個原子更新基本類型,以AtomicInteger為例,其他兩種基本類似。以下是AtomicInteger囊括的大致方法

    //給AtomicInteger設置newValue并返回加oldValue
public final int getAndSet(int newValue) //如果輸入的值和期望值相等就set并返回true/false
public final boolean compareAndSet(int expect, int update) //對AtomicInteger原子的加1并返回當前自增前的value
public final int getAndIncrement() //對AtomicInteger原子的減1并返回自減之前的的value
public final int getAndDecrement() //對AtomicInteger原子的加上delta值并返加之前的value
public final int getAndAdd(int delta) //對AtomicInteger原子的加1并返回加1后的值
public final int incrementAndGet() //對AtomicInteger原子的減1并返回減1后的值
public final int decrementAndGet() //給AtomicInteger原子的加上指定的delta值并返回加后的值
public final int addAndGet(int delta)

以getAndIncrement為例看下源碼

public final int getAndIncrement() {
for (;;) {  // 若 compareAndSet 為 false, 則一直循環查詢
//先取出AtomicInteger的當前值
int current = get();
//對當前值加1操作
int next = current + 1;
//這里很關鍵,通過compareAndSet方法比較當前值有沒有被其它線程修改
if (compareAndSet(current, next))
return current;
}
}

compareAndSet方法里面是調用了Unsafe類的compareAndSwapInt方法(后邊討論)

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

以下先介紹 Unsafe 類:


2、Unsafe
  作用:Unsafe類提供了硬件級別的原子操作,提高了Java對底層操作的能力。
  來自 sun.misc 包,Java最初被設計為一種安全的受控環境,但是 sun.misc.Unsafe 是 Java HotSpot 提供的操作內存和線程的"后門",提供了一些可以直接操控內存和線程的底層操作。
  Unsafe被JDK廣泛應用于 java.nio 和并發包等實現中,這個不安全的類提供了一個觀察 HotSpot JVM 內部結構并且可以對其進行修改,但是不建議在生產環境中使用   先看部分 Unsafe 源碼:
    private Unsafe() {  // 私有
} private static final Unsafe theUnsafe = new Unsafe(); @CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}

  1、獲取Unsafe實例 

  • 不能直接new Unsafe(),原因是Unsafe被設計成單例模式,構造方法是私有的;
  • 不能通過調用Unsafe.getUnsafe()獲取,因為getUnsafe被設計成只能從引導類加載器(bootstrap class loader)加載

  Unsafe 實例獲取方法:

//方法一:我們可以令我們的代碼“受信任”。運行程序時,使用bootclasspath選項,指定系統類路徑加上你使用的一個Unsafe路徑(但這太難了。)
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient // 方法二: 使用反射,Unsafe類包含一個私有的、名為theUnsafe的實例,我們可以通過Java反射竊取該變量。
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe) field.get(null);
} catch (Exception e) {
}
}

  

  2、Unsafe API

  sun.misc.Unsafe類包含105個方法。實際上,對各種實體操作有幾組重要方法,其中的一些如下:

Info.僅返回一些低級的內存信息

  • addressSize
  • pageSize

Objects.提供用于操作對象及其字段的方法

  • allocateInstance
  • objectFieldOffset

Classes.提供用于操作類及其靜態字段的方法

  • staticFieldOffset
  • defineClass
  • defineAnonymousClass
  • ensureClassInitialized

Arrays.操作數組

  • arrayBaseOffset
  • arrayIndexScale

Synchronization.低級的同步原語

  • monitorEnter
  • tryMonitorEnter
  • monitorExit
  • compareAndSwapInt
  • putOrderedInt

Memory.直接內存訪問方法

  • allocateMemory
  • copyMemory
  • freeMemory
  • getAddress
  • getInt
  • putInt

3、有趣的用例

  ①、避免初始化

   當你想要跳過對象初始化階段,或繞過構造器的安全檢查,或實例化一個沒有任何公共構造器的類,allocateInstance方法是非常有用的。考慮以下類:

class A {
private long a; // not initialized value public A() {
this.a = 1; // initialization
} public long a() { return this.a; }
}

使用構造器、反射和unsafe初始化它,將得到不同的結果。

A o1 = new A(); // constructor
o1.a(); // prints 1 A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1 A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0

  

Unsafe 的 allocateInstance 方法可以繞過構造方法直接創建對象

objectFieldOffset 方法可以為對象屬性賦值。

    public static void main(String[] args) {
try {
Customer customer = (Customer) UNSAFE.allocateInstance(Customer.class);
customer.setName("King");
System.out.println(customer.toString()); Field name = customer.getClass().getDeclaredField("name");
UNSAFE.putObject(customer, UNSAFE.objectFieldOffset(name), "LoLa");
System.out.println(customer.toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

  ②、內存崩潰(Memory corruption)

  這對于每個C程序員來說是常見的。順便說一下,它是繞過安全的常用技術。

  考慮下那些用于檢查“訪問規則”的簡單類:

class Guard {
private int ACCESS_ALLOWED = 1; public boolean giveAccess() {
return 42 == ACCESS_ALLOWED;
}
}

  客戶端代碼是非常安全的,并且通過調用giveAccess()來檢查訪問規則。可惜,對于客戶,它總是返回false。只有特權用戶可以以某種方式改變ACCESS_ALLOWED常量的值并且得到訪問

  實際上,這并不是真的。演示代碼如下:

Guard guard = new Guard();
guard.giveAccess(); // false, no access // bypass
Unsafe unsafe = getUnsafe();
Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption guard.giveAccess(); // true, access granted

  ③、并發(Concurrency)

  compareAndSwap 方法是原子的,并且可用來實現高性能的、無鎖的數據結構。

  比如,考慮問題:在使用大量線程的共享對象上增長值。

  

  首先,我們定義簡單的Counter接口:

  

interface Counter {
void increment();
long getCounter();
}

然后,我們定義使用Counter的工作線程CounterClient

class CounterClient implements Runnable {
private Counter c;
private int num; public CounterClient(Counter c, int num) {
this.c = c;
this.num = num;
} @Override
public void run() {
for (int i = 0; i < num; i++) {
c.increment();
}
}
}

測試代碼:

int NUM_OF_THREADS = 1000;
int NUM_OF_INCREMENTS = 100000;
ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS);
Counter counter = ... // creating instance of specific counter
long before = System.currentTimeMillis();
for (int i = 0; i < NUM_OF_THREADS; i++) {
service.submit(new CounterClient(counter, NUM_OF_INCREMENTS));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
long after = System.currentTimeMillis();
System.out.println("Counter result: " + c.getCounter());
System.out.println("Time passed in ms:" + (after - before));

第一個無鎖版本的計數器:

運行快,但沒有線程管理,結果是不準確的。

class StupidCounter implements Counter {
private long counter = 0; @Override
public void increment() {
counter++;
} @Override
public long getCounter() {
return counter;
}
}

輸出:

Counter result: 99542945
Time passed in ms: 679

第二次嘗試,添加上最簡單的java式同步:

激進的同步有效,但耗時長。

class SyncCounter implements Counter {
private long counter = 0; @Override
public synchronized void increment() {
counter++;
} @Override
public long getCounter() {
return counter;
}
}

輸出:

Counter result: 100000000
Time passed in ms: 10136

第三種,試試ReentrantReadWriteLock

仍然正確,耗時較短。

class LockCounter implements Counter {
private long counter = 0;
private WriteLock lock = new ReentrantReadWriteLock().writeLock(); @Override
public void increment() {
lock.lock();
counter++;
lock.unlock();
} @Override
public long getCounter() {
return counter;
}
}

第四種,atomics的運行效果如何?

AtomicCounter的運行結果更好。

class AtomicCounter implements Counter {
AtomicLong counter = new AtomicLong(0); @Override
public void increment() {
counter.incrementAndGet();
} @Override
public long getCounter() {
return counter.get();
}
}

第五種,試試Unsafe原始的compareAndSwapLong,看看它是否真的只有特權才能使用它?

class CASCounter implements Counter {
private volatile long counter = 0;
private Unsafe unsafe;
private long offset; public CASCounter() throws Exception {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
} @Override
public void increment() {
long before = counter;
while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
before = counter;
}
} @Override
public long getCounter() {
return counter;
}
}

第四種、第五種時間大致一樣,Unsafe 看起來似乎等價于atomics。atomics使用Unsafe?(是的)

3、 CAS

  1、CAS介紹

    AtomicInteger的核心就是一個CAS算法(CompareAndSwap),比較并交換算法,此算法是由unsafe的底層代碼實現,它是一個原子的操作。 (CAS能很高效的解決原子操作)

    原理就是:如果內存中的實際值與update值相同,則將實際值更新為expect值,反之則返回失敗,由上層系統循環獲取實際值后,再次調用此CAS算法。

    

    CAS 是設計并發算法時常用到的一種技術,java.util.concurrent包完全建立在CAS之上。

    CAS是通過Unsafe實現的,java.util.concurrent包中借助CAS實現了區別于synchronouse同步鎖的一種樂觀鎖。

    看下Unsafe下的三個方法:

public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);

public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);

public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);

  

  2、CAS 在 Unsafe 中應用

  以  compareAndSwapInt 為例, CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。

  unsafe.compareAndSwapInt(this, this, valueOffset, expect, update),包含 ①比較②更新兩個操作,compareAndSwapInt如何這兩個步驟的原子性呢?

  

  3、CAS原理

   CAS通過調用JNI的代碼實現的。JNI:Java Native Interface為JAVA本地調用(用 native 關鍵字標注),允許java調用其他語言。

   而compareAndSwapInt就是借助C來調用CPU底層指令實現的。

三、總結

  這是我認真看了一天博客摘抄下來的整理,我要保持每天不斷的學習,爭取早日寫出有水平的干貨

總結

以上是生活随笔為你收集整理的1. AtomicInteger 、Unsafe 及 CAS方法的整理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。