1.大纲
作用
6类原子类
Atomic*基本类型的原子类
Atomic*Array数组类型原子类
Atomic*Reference引用类型原子类
普通变量升级为原子类
Adder累加器
Accumulator累加器
一:作用
1.说明
一个操作是不可中断的,即使是多线程的情况下也可以保证
为了保证并发下的线程安全
2.主要特点
粒度更细,原子变量可以把竞争范围缩小到变量级别
效率更高,使用原子类比使用锁的效率更高,除了高度竞争的情况
二:六类原子类
1.
三:基本型原子类
1.AtomicInteger常用方法
get:获取当前的值
getAndSet:获取当前的值,并设置新的值
getAndIncrement:获取当前的值,并自增
getAndDecremrnt:获取当前值,并自减
getAndAdd(n):获取当前的值,并加上预期的值
boolean compareAndSet(sepect,update):如果输入的等于预期值,则以原子方式将该值设置为输入值
2.举例
package com.jun.juc.lock.atomic; import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerDemo implements Runnable{ private static final AtomicInteger atomicInteger = new AtomicInteger(); private static volatile int basicCount = 0; public void invrementAtomic(){ atomicInteger.getAndIncrement(); } public void incrementBasic(){ basicCount ++ ; } @Override public void run() { for (int i=0;i<10000;i++){ invrementAtomic(); incrementBasic(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo demo = new AtomicIntegerDemo(); Thread th1 = new Thread(demo); Thread th2 = new Thread(demo); th1.start(); th2.start(); th1.join(); th2.join(); System.out.println("原子类的结果是"+atomicInteger.get()); System.out.println(basicCount); } }
效果:
Disconnected from the target VM, address: '127.0.0.1:59679', transport: 'socket' 原子类的结果是20000 19097 Process finished with exit code 0
四:AtomicArray
1.示例
package com.jun.juc.lock.atomic; import java.util.concurrent.atomic.AtomicIntegerArray; public class AtomicArrayDemo { public static void main(String[] args) { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); // 初始化 Thread[] threadsIn = new Thread[100]; Thread[] threadsDe = new Thread[100]; // Decrement decrement = new Decrement(atomicIntegerArray); Increment increment = new Increment(atomicIntegerArray); for (int i=0;i<100;i++){ threadsDe[i] = new Thread(decrement); threadsIn[i] = new Thread(increment); threadsDe[i].start(); threadsIn[i].start(); } for (int i=0;i<100;i++){ try { threadsDe[i].join(); threadsIn[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i=0; i<atomicIntegerArray.length();i++){ if(atomicIntegerArray.get(i)!=0){ System.out.println("有问题"+i); } } System.out.println("结束"); } } class Decrement implements Runnable{ private AtomicIntegerArray atomicIntegerArray; public Decrement(AtomicIntegerArray atomicIntegerArray){ this.atomicIntegerArray = atomicIntegerArray; } @Override public void run() { for (int i=0;i<atomicIntegerArray.length();i++){ atomicIntegerArray.getAndDecrement(i); } } } class Increment implements Runnable{ private AtomicIntegerArray atomicIntegerArray; public Increment(AtomicIntegerArray atomicIntegerArray){ this.atomicIntegerArray = atomicIntegerArray; } @Override public void run() { for (int i=0;i<atomicIntegerArray.length();i++){ atomicIntegerArray.getAndIncrement(i); } } }
效果:
Connected to the target VM, address: '127.0.0.1:55387', transport: 'socket' Disconnected from the target VM, address: '127.0.0.1:55387', transport: 'socket' 结束 Process finished with exit code 0
五:引用类型的原子类
1.说明
可以让一个对象保证原子性
2.示例
以前写过的自旋锁,可以保证对象的安全性
package com.jun.juc.lock.spinlock; import java.util.concurrent.atomic.AtomicReference; /** * 自旋锁 */ public class SpinLock { private AtomicReference<Thread> sign = new AtomicReference<Thread>(); public void lock(){ Thread current = Thread.currentThread(); while (!sign.compareAndSet(null, current)){ System.out.println(Thread.currentThread().getName() + "获取失败,再次尝试"); } } public void unlock(){ Thread current = Thread.currentThread(); sign.compareAndSet(current, null); } public static void main(String[] args) { SpinLock spinLock = new SpinLock(); Runnable runnable = new Runnable(){ public void run(){ System.out.println(Thread.currentThread().getName() + "尝试获取自旋锁"); spinLock.lock(); System.out.println(Thread.currentThread().getName() +"获取到了自旋锁"); try{ Thread.sleep(300); }catch (Exception e){ e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName() +"释放到了自旋锁"); spinLock.unlock(); } } }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); thread2.start(); } }
六:原子类
1.Adder累加器
是java8新引入的
并发下,LongAdder比ATomICLong的效果更高,是空间换时间
在竞争激烈的情况下,LongAdder把不同的线程对象到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发
2.AtomicLong
这里使用了shutdown方法
package com.jun.juc.lock.atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; public class AtomicLongDemo { public static void main(String[] args) { AtomicLong atomicLong = new AtomicLong(0); ExecutorService executorService = Executors.newFixedThreadPool(20); long start = System.currentTimeMillis(); for (int i=0; i<10000;i++){ executorService.submit(new Task(atomicLong)); } executorService.shutdown(); while (!executorService.isTerminated()){ // 没有完成,则进行等待 } long end = System.currentTimeMillis(); System.out.println(end-start); } private static class Task implements Runnable{ private AtomicLong counter; public Task(AtomicLong counter){ this.counter = counter; } @Override public void run() { for (int i=0;i<10000;i++){ counter.incrementAndGet(); } } } }
效果:
Connected to the target VM, address: '127.0.0.1:56230', transport: 'socket' Disconnected from the target VM, address: '127.0.0.1:56230', transport: 'socket' 1915 Process finished with exit code 0
3.LongAdder的使用
package com.jun.juc.lock.atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; public class LongAdderDemo { public static void main(String[] args) { LongAdder longAdder = new LongAdder(); ExecutorService executorService = Executors.newFixedThreadPool(20); long start = System.currentTimeMillis(); for (int i=0; i<10000;i++){ executorService.submit(new Task(longAdder)); } executorService.shutdown(); while (!executorService.isTerminated()){ // 没有完成,则进行等待 } long end = System.currentTimeMillis(); System.out.println(end-start); } private static class Task implements Runnable{ private LongAdder counter; public Task(LongAdder counter){ this.counter = counter; } @Override public void run() { for (int i=0;i<10000;i++){ counter.increment(); } } } }
效果:
Connected to the target VM, address: '127.0.0.1:56388', transport: 'socket' Disconnected from the target VM, address: '127.0.0.1:56388', transport: 'socket' 207 Process finished with exit code 0
4.原因
AtomicLong每次都要flush和refesh,每一次加法都要进行同步,所以在高并发的时候导致冲突比较多,就降低了效率
LongAdder,在每个线程中都有自己的一个计数器,仅用来在自己的线程内计数,这样一来就不会和其他线程计数器进行干扰
要咋汇总呢?
LongAdder引入了分段累加的概念,内部有一个base、变量和Cell【】数组共同参与计数:
base变量:竞争不激烈,直接累加到该变量上
Cell:竞争激烈,各个线程分散累加到自己的槽Cell[i]中
5.sum源码
public long sum() { Cell[] as = cells; Cell a; long sum = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
但是有点小问题:
每个小cell可能都有变化,导致sum不准
6.适用场合
LongAdder适用的场景是统计求和,只提供了add方法
AtomicLong还具有cas方法
七:Accumulator累加器
1.与Adder相比较
更加的通用
2.示例
可以发现很灵活
package com.jun.juc.lock.atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAccumulator; import java.util.stream.IntStream; public class LongAccumulatorDemo { public static void main(String[] args) { LongAccumulator accumulator = new LongAccumulator((x,y)-> x + y, 1); ExecutorService executorService = Executors.newFixedThreadPool(8); IntStream.range(1, 10).forEach(i -> executorService.submit(()-> accumulator.accumulate(i))); executorService.shutdown(); while (!executorService.isTerminated()){ } System.out.println(accumulator.getThenReset()); } }
效果:
Connected to the target VM, address: '127.0.0.1:57694', transport: 'socket' Disconnected from the target VM, address: '127.0.0.1:57694', transport: 'socket' 46 Process finished with exit code 0
3.另一个特征
可以发现,上面是可以多线程的计算
同时,对顺序有没要求