原子變量類基于CAS實現的, 當對共享變量進行read-modify-write更新操作時,通過原子變量類可以保障操作的原子性與可見性.對變量的read-modify-write更新操作是指當前操作不是一個簡單的賦值,而是變量的新值依賴變量的舊值,如自增操作i++. 由于volatile只能保證可見性,無法保障原子性, 原子變量類內部就是借助一個Volatile變量,并且保障了該變量的read-modify-write操作的原子性, 有時把原子變量類看作增強的volatile變量. 原子變量類有12個,如:
分組 | 原子變量類 |
---|---|
基礎數據型 | AtomicInteger, AtomicLong, AtomicBoolean |
數組型 | AtomicIntegerArray, AtomicLongArray,AtomicReferenceArray |
字段更新器 | AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater |
引用型 | AtomicReference,AtomicStampedReference, AtomicMarkableReference |
AtomicLong
package com.wkcto.atomics.atomiclong;
import java.util.concurrent.atomic.AtomicLong;
/**
* 使用原子變量類定義一個計數器
* 該計數器,在整個程序中都能使用,并且所有的地方都使用這一個計數器,這個計數器可以設計為單例
* 北京動力節點老崔
*/
public class Indicator {
//構造方法私有化
private Indicator(){}
//定義一個私有的本類靜態的對象
private static final Indicator INSTANCE = new Indicator();
//3)提供一個公共靜態方法返回該類唯一實例
public static Indicator getInstance(){
return INSTANCE;
}
//使用原子變量類保存請求總數,成功數,失敗數
private final AtomicLong requestCount = new AtomicLong(0); //記錄請求總數
private final AtomicLong successCount = new AtomicLong(0); //處理成功總數
private final AtomicLong fialureCount = new AtomicLong(0); //處理失敗總數
//有新的請求
public void newRequestReceive(){
requestCount.incrementAndGet();
}
//處理成功
public void requestProcessSuccess(){
successCount.incrementAndGet();
}
//處理失敗
public void requestProcessFailure(){
fialureCount.incrementAndGet();
}
//查看總數,成功數,失敗數
public long getRequestCount(){
return requestCount.get();
}
public long getSuccessCount(){
return successCount.get();
}
public long getFailureCount(){
return fialureCount.get();
}
}
package com.wkcto.atomics.atomiclong;
import java.util.Random;
/**
* 模擬服務器的請求總數, 處理成功數,處理失敗數
* 北京動力節點老崔
*/
public class Test {
public static void main(String[] args) {
//通過線程模擬請求,在實際應用中可以在ServletFilter中調用Indicator計數器的相關方法
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
//每個線程就是一個請求,請求總數要加1
Indicator.getInstance().newRequestReceive();
int num = new Random().nextInt();
if ( num % 2 == 0 ){ //偶數模擬成功
Indicator.getInstance().requestProcessSuccess();
}else { //處理失敗
Indicator.getInstance().requestProcessFailure();
}
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印結果
System.out.println( Indicator.getInstance().getRequestCount()); //總的請求數
System.out.println( Indicator.getInstance().getSuccessCount()); //成功數
System.out.println( Indicator.getInstance().getFailureCount()); //失敗數
}
}
AtomicIntegerArray
原子更新數組
package com.wkcto.atomics.atomicarray;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray的基本操作
* 原子更新數組
* 北京動力節點老崔
*/
public class Test {
public static void main(String[] args) {
//1)創建一個指定長度的原子數組
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
System.out.println( atomicIntegerArray ); //[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
//2)返回指定位置的元素
System.out.println( atomicIntegerArray.get(0)); //0
System.out.println( atomicIntegerArray.get(1)); //0
//3)設置指定位置的元素
atomicIntegerArray.set(0, 10);
//在設置數組元素的新值時, 同時返回數組元素的舊值
System.out.println( atomicIntegerArray.getAndSet(1, 11) ); //0
System.out.println( atomicIntegerArray ); //[10, 11, 0, 0, 0, 0, 0, 0, 0, 0]
//4)修改數組元素的值,把數組元素加上某個值
System.out.println( atomicIntegerArray.addAndGet(0, 22) ); //32
System.out.println( atomicIntegerArray.getAndAdd(1, 33)); //11
System.out.println( atomicIntegerArray ); //[32, 44, 0, 0, 0, 0, 0, 0, 0, 0]
//5)CAS操作
//如果數組中索引值為0的元素的值是32 , 就修改為222
System.out.println( atomicIntegerArray.compareAndSet(0, 32, 222)); //true
System.out.println( atomicIntegerArray ); //[222, 44, 0, 0, 0, 0, 0, 0, 0, 0]
System.out.println( atomicIntegerArray.compareAndSet(1, 11, 333)); //false
System.out.println(atomicIntegerArray);
//6)自增/自減
System.out.println( atomicIntegerArray.incrementAndGet(0) ); //223, 相當于前綴
System.out.println( atomicIntegerArray.getAndIncrement(1)); //44, 相當于后綴
System.out.println( atomicIntegerArray ); //[223, 45, 0, 0, 0, 0, 0, 0, 0, 0]
System.out.println( atomicIntegerArray.decrementAndGet(2)); //-1
System.out.println( atomicIntegerArray); //[223, 45, -1, 0, 0, 0, 0, 0, 0, 0]
System.out.println( atomicIntegerArray.getAndDecrement(3)); //0
System.out.println( atomicIntegerArray ); //[223, 45, -1, -1, 0, 0, 0, 0, 0, 0]
}
}
package com.wkcto.atomics.atomicarray;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* 在多線程中使用AtomicIntegerArray原子數組
* 北京動力節點老崔
*/
public class Test02 {
//定義原子數組
static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
public static void main(String[] args) {
//定義線程數組
Thread[] threads = new Thread[10];
//給線程數組元素賦值
for (int i = 0; i < threads.length; i++) {
threads[i] = new AddThread();
}
//開啟子線程
for (Thread thread : threads) {
thread.start();
}
//在主線程中查看自增完以后原子數組中的各個元素的值,在主線程中需要在所有子線程都執行完后再查看
//把所有的子線程合并到當前主線程中
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println( atomicIntegerArray );
}
//定義一個線程類,在線程類中修改原子數組
static class AddThread extends Thread{
@Override
public void run() {
//把原子數組的每個元素自增1000次
for (int j = 0; j < 100000; j++) {
for (int i = 0; i < atomicIntegerArray.length(); i++) {
atomicIntegerArray.getAndIncrement(i % atomicIntegerArray.length());
}
}
/* for (int i = 0; i < 10000; i++) {
atomicIntegerArray.getAndIncrement(i % atomicIntegerArray.length());
}*/
}
}
}
AtomicIntegerFieldUpdater
AtomicIntegerFieldUpdater可以對原子整數字段進行更新,要求:
● 字符必須使用volatile修飾,使線程之間可見。
● 只能是實例變量,不能是靜態變量,也不能使用final修飾。
package com.wkcto.atomics.atominintegerfiled;
/**
* 使用AtomicIntegerFieldUpdater更新的字段必須使用volatile修飾
* 北京動力節點老崔
*/
public class User {
int id;
volatile int age;
public User(int id, int age) {
this.id = id;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
'}';
}
}
package com.wkcto.atomics.atominintegerfiled;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* 線程類,
* 北京動力節點老崔
*/
public class SubThread extends Thread {
private User user; //要更新的User對象
//創建AtomicIntegerFieldUpdater更新器
private AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
public SubThread(User user) {
this.user = user;
}
@Override
public void run() {
//在子線程中對user對象的age字段自增10次
for (int i = 0; i < 10; i++) {
System.out.println( updater.getAndIncrement(user));
}
}
}
package com.wkcto.atomics.atominintegerfiled;
/**
* 北京動力節點老崔
*/
public class Test {
public static void main(String[] args) {
User user = new User(1234, 10);
//開啟10個線程
for (int i = 0; i < 10; i++) {
new SubThread(user).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( user );
}
}
AtomicReference
可以原子讀寫一個對象
package com.wkcto.atomics.atomicreference;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
/**
* 使用AtomicReference原子讀寫一個對象
* 北京動力節點老崔
*/
public class Test01 {
//創建一個AtomicReference對象
static AtomicReference<String> atomicReference = new AtomicReference<>("abc");
public static void main(String[] args) throws InterruptedException {
//創建100個線程修改字符串
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(20));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (atomicReference.compareAndSet("abc","def")){
System.out.println(Thread.currentThread().getName() + "把字符串abc更改為def");
}
}
}).start();
}
//再創建100個線程
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(20));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (atomicReference.compareAndSet("def","abc")){
System.out.println(Thread.currentThread().getName() + "把字符串還原為abc");
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(atomicReference.get());
}
}
package com.wkcto.atomics.atomicreference;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 演示AtomicReference可能會出現CAS的ABA問題
* 北京動力節點老崔
*/
public class Test02 {
private static AtomicReference<String> atomicReference = new AtomicReference<>("abc");
public static void main(String[] args) throws InterruptedException {
//創建第一個線程,先把abc字符串改為"def",再把字符串還原為abc
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
atomicReference.compareAndSet("abc", "def");
System.out.println(Thread.currentThread().getName() + "--" + atomicReference.get());
atomicReference.compareAndSet("def", "abc");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( atomicReference.compareAndSet("abc", "ghg"));
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println( atomicReference.get());
}
}
package com.wkcto.atomics.atomicreference;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* AtomicStampedReference原子類可以解決CAS中的ABA問題
* 在AtomicStampedReference原子類中有一個整數標記值stamp, 每次執行CAS操作時,需要對比它的版本,即比較stamp的值
* 北京動力節點老崔
*/
public class Test03 {
// private static AtomicReference<String> atomicReference = new AtomicReference<>("abc");
//定義AtomicStampedReference引用操作"abc"字符串,指定初始化版本號為0
private static AtomicStampedReference<String> stampedReference = new AtomicStampedReference<>("abc", 0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stampedReference.compareAndSet("abc", "def", stampedReference.getStamp(), stampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName() + "--" +stampedReference.getReference());
stampedReference.compareAndSet("def", "abc", stampedReference.getStamp(), stampedReference.getStamp()+1);
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
int stamp = stampedReference.getStamp(); //獲得版本號
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( stampedReference.compareAndSet("abc", "ggg", stamp, stamp+1));
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println( stampedReference.getReference() );
}
}