這個是在學習工作中的一些總結,若有不對之處歡迎大家指出。侵刪!
需要源碼聯繫QQ:1352057131
得之在俄頃,積之在平日。
原子類的作用
原子類和鎖的作用類似,都是爲了保證併發情況下的線程安全,不過原子類相比於鎖有一定的優勢:
粒度更細:原子變量可以把競爭範圍縮小到變量級別。
效率更高:使用原子類的效率比使用鎖的效率更高,但是高競爭下的情況除外。
原子類概覽
Atomic*基本類型原子類
AtomicInteger常用方法
//獲取當前的值並加上預期的值
atomicInteger.getAndAdd(int value);
//獲取當前值
atomicInteger.get();
//獲取當前值並自減
atomicInteger.getAndDecrement();
//獲取當前值並自增
atomicInteger.getAndIncrement();
//獲取當前值並設置新值
atomicInteger.getAndSet(int value);
//如果輸入的值等於預期值,則以原子的方式將該值設置爲輸入值(update)
atomicInteger.compareAndSet(int value,int update);
AtomicInteger示例
public class AtomicTest implements Runnable{
private static AtomicInteger atomicInteger = new AtomicInteger(0);
private static int a = 0;
public static void Atomicadd(){
atomicInteger.getAndIncrement();
}
public static void add(){
a++;
}
public static void main(String[] args) throws InterruptedException {
Thread thread0 = new Thread(new AtomicTest());
Thread thread1 = new Thread(new AtomicTest());
thread0.start();
thread1.start();
thread0.join();
thread1.join();
System.out.println("atomicInteger="+atomicInteger);
System.out.println("a="+a);
}
@Override
public void run() {
for (int i = 0; i <100000 ; i++) {
Atomicadd();
add();
}
}
}
運行結果:
從圖中可以看出AtomicInteger是線程安全的
Atomic*Array數組類型原子類
AtomicIntegerArray示例
public class AtomicTest{
//聲明長度爲100的AtomicIntegerArray
private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(100);
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(new Add(atomicIntegerArray));
executorService.submit(new Dec(atomicIntegerArray));
Thread.sleep(1000);
executorService.shutdown();
for (int i = 0; i <atomicIntegerArray.length() ; i++) {
if (atomicIntegerArray.get(i)!=0){
System.out.println("發現不等於0的數");
}
}
System.out.println("執行完畢");
}
}
class Add implements Runnable{
private AtomicIntegerArray atomicIntegerArray;
public Add(AtomicIntegerArray atomicIntegerArray) {
this.atomicIntegerArray = atomicIntegerArray;
}
@Override
public void run() {
for (int i = 0; i < atomicIntegerArray.length(); i++) {
//加1
atomicIntegerArray.getAndIncrement(i);
}
}
}
class Dec implements Runnable{
private AtomicIntegerArray atomicIntegerArray;
public Dec(AtomicIntegerArray atomicIntegerArray) {
this.atomicIntegerArray = atomicIntegerArray;
}
@Override
public void run() {
for (int i = 0; i < atomicIntegerArray.length(); i++) {
//減1
atomicIntegerArray.getAndDecrement(i);
}
}
}
Atomic*Reference引用類型原子類
AtomicReference示例
Atomic*FieldUpdater升級類型原子類
AtomicIntegerFieldUpdater示例
public class AtomicIntegerFieldUpdaterTest implements Runnable{
//第一個參數爲類 第二個參數爲要升級的變量名
AtomicIntegerFieldUpdater<User> aifu = AtomicIntegerFieldUpdater.newUpdater(User.class,"number");
static User tom = new User();
static User tony = new User();
public static void main(String[] args) throws InterruptedException {
AtomicIntegerFieldUpdaterTest aifut = new AtomicIntegerFieldUpdaterTest();
for (int i = 0; i < 1000; i++) {
Thread thread0 = new Thread(aifut);
Thread thread1 = new Thread(aifut);
thread0.start();
thread1.start();
thread0.join();
thread1.join();
}
System.out.println("tom"+tom.number);
System.out.println("tony"+tony.number);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
tom.number++;
aifu.getAndDecrement(tony);
}
}
static class User{
volatile int number;//分數
}
}
Adder累加器
爲什麼要使用Adder
高併發下LongAdder比AtomicLong效率高,不過本質是空間換時間。
競爭激烈的時候,LongAdder把不同的線程對應到不同的Cell上進行修改,降低了衝突的概率,事多段鎖的理念,提高了併發性。
LongAdder示例
public class AdderTest implements Runnable{
private static LongAdder longAdder = new LongAdder();
public void Adder_Add(){
longAdder.increment();
}
@Override
public void run() {
Adder_Add();
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i <10000 ; i++) {
executorService.submit(new AdderTest());
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
System.out.println(longAdder);
}
LongAdder與AtomicLong性能對比
LongAdder:
```java
public class AdderTest implements Runnable{
private static LongAdder longAdder = new LongAdder();
public void Adder_Add(){
for (int i = 0; i < 1000; i++) {
longAdder.increment();
}
}
@Override
public void run() {
Adder_Add();
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i <100000 ; i++) {
executorService.submit(new AdderTest());
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
long end = System.currentTimeMillis();
System.out.println("執行時間:"+(end-start));
}
}
AtomicLong:
public class AtomicLongTest implements Runnable{
private static AtomicLong atomicLong = new AtomicLong(0);
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
atomicLong.getAndIncrement();
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i <100000 ; i++) {
executorService.submit(new AtomicLongTest());
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
long end = System.currentTimeMillis();
System.out.println("執行時間:"+(end-start));
}
}
從運行結果可以看出LongAdder的效率明顯要高很多,原因如下:
AtomicLong每次加法都要flush和refresh導致很耗費資源
從圖中可以看出AtomicLong每次加法都要做同步,在高併發的情況下會導致衝突比較多因此效率下降,而LongAdder每個線程都會有自己的計數器,僅用來在自己線程內計數,這樣就不會受到其他線程計數器的干擾。如下圖
從圖中可以看出,第一個線程的計數器數值,也就是str’,爲1的時候,可能線程2的計數器ctr”的數值已經是3了,他們之間並不存在競爭關係,所以在加和的過程中根本不需要同步機制,也不需要flush和refresh。也沒有公共的counter來計數。只是到最後sum的時候纔會同步將所有的數值累加起來 。
適用場景
適用於統計求和計數的場景,而且LongAdder基本只提供了add方法,AtomicLong還具有cas操作。