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