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.另一个特征
可以发现,上面是可以多线程的计算
同时,对顺序有没要求