多线程九 原子类
Posted zhuchangwu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程九 原子类相关的知识,希望对你有一定的参考价值。
JDK5提供原子类,及其操作
AtomicXXX是通过CAS , Unsafe.compareAndSwapInt实现的
简单分析一下原子类是如果使用cas,无锁却保证线程安全的
每条线程都有自己的本地缓存,他们要想操作变量,首先是把变量复制到自己的缓存中,然后处理数据,数据处理结束后,将自己缓存的数据刷新会主存,而CAS则就是利用这一点实现的
下面是看Unsafe.class源码,可以看到,下面几个cas方法,全部是native,也就是本地方法
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
随便找一个方法
//var1 就是方法的调用者 var2:当前的值 var4需要加的值
public final int getAndAddInt(Object var1, long var2, int var4)
int var5;
do
//此方法(同样也是本地方法),返回当前 内存中var1对象里面的 var2的值
var5 = this.getIntVolatile(var1, var2);
//判断var1里面的值var2是否就是var5,如果相等我们更新为 var5+var4 ,否则继续循环尝试修改
while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
如何使用呢?
AtomicLong atomicLong = new AtomicLong();
atomicLong.getAndIncrement();
解释一下,为了保证数据的安全性,根据JMM内存模型设计出了cas算法,compareAndSwap先比较,再更换值,现在看我们上面随便找的方法, 里面有一个do-while循环,其实基本上所有设及cas算法的方法,都采用了这个循环,它保证我们可以做出足够多次数的判断
原子更新boolean
下面使用它完成确保某段代码只执行一次
public class demo
// 原子更新boolean 初始化为false
public static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
//请求总数
public static int clientTotal = 5000;
//同时并发执行的线程数
public static int threadPool = 200;
public static void main(String[] args) throws InterruptedException
ExecutorService executor = Executors.newCachedThreadPool();
//每次两百次并发
Semaphore semaphore = new Semaphore(threadPool);
//计数器 执行五千次
CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i=0;i<clientTotal;i++)
executor.execute(new Runnable()
@Override
public void run()
try
semaphore.acquire();
text(); // 我们只想执行一次的方法
semaphore.release();
countDownLatch.countDown(); //执行一次,计数减少一次
catch (InterruptedException e)
e.printStackTrace();
);
countDownLatch.await(); //只要计数不为零,就等待...
executor.shutdown(); //关闭线程池
System.out.println("atomicBoolean=="+atomicBoolean.get());
public static void text()
if (atomicBoolean.compareAndSet(false,true))
System.out.println("值更新了");
查看结果,哪怕执行5000次,每次200的并发,if()里面的代码还是只执行了一次
值更新了
atomicBoolean==true
类名 | 描述 |
---|---|
AtomicInteger | 用原子方式更新的 int 值 |
AtomicLong | 用原子方式更新的 long 值 |
AtomicBoolean | 原子方式更新的 boolean 值 |
- 原子更新基本类型
类名 | 描述 |
---|---|
AtomicInteger | 用原子方式更新的 int 值 |
AtomicLong | 用原子方式更新的 long 值 |
AtomicBoolean | 原子方式更新的 boolean 值 |
- 原子更新数组
类名 | 描述 | |
---|---|---|
AtomicIntegerArray | 用原子方式更新其元素的 int 数组 | |
AtomicLongArray | 用原子方式更新其元素的 long 数组 |
- 原子更新抽象类型
类名 | 描述 | |
---|---|---|
AtomicReference |
用原子方式更新的对象引用 | |
AtomicStamptedReference | 增加了版本号机制,解决了CAS里面的ABA问题 |
...
注意: 针对的是V型的对象,而不是对象里面的字段
- 原子更新字段
类名 | 描述 |
---|---
AtomicIntegerFieldUpdater|基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新
|AtomicLongFieldUpdater|基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新
注意: 针对的是V型的对象,而不是对象里面的字段
JDK8 新增原子类:
- DoubleAdder
LongAdder 弥补AtomicLong在高并发情况下的性能低问题
AtomicLong里面仅仅有一个Long类型的值,多个线程并发访问,当一个线程获取到锁修改Long值时,很容易成功,但是当很多个线程并发修改它的值,很可能陷入do-while死循环,消耗性能
LongAdder里面有一组值,每个值都是一个cell,所有的cell加起来是一个base,例如Base如果是5,那么cell可能被分解成2 3,将原来修改一点的高压力分散在不同点上,多线程并发访问去抢占修改cell.在sum()方法中汇总,但线程的话,直接得出结果,从而提高性能
...
原子类并不完全安全!
**虽然addAndGet()方法是原子的,多线程中,方法的调用是无序的
public class TextAtomicService
public static AtomicLong atomicLong = new AtomicLong();
public void add()
System.out.println(Thread.currentThread().getName()+"加了100之后的值为:"+atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
//
public class AtomicText extends Thread
private TextAtomicService textAtomicService;
public AtomicText(TextAtomicService t1)
textAtomicService=t1;
@Override
public void run()
textAtomicService.add();
public static void main(String[] args)
TextAtomicService textAtomicService = new TextAtomicService();
AtomicText[] atomicTexts = new AtomicText[5];
try
for (int i=0;i<5;i++)
atomicTexts[i]=new AtomicText(textAtomicService);
for (int i=0;i<5;i++)
atomicTexts[i].start();
Thread.sleep(2000);
catch (InterruptedException e)
e.printStackTrace();
可能出现这种情况,就是当前线程执行完addAndGet(100)之后,cpu的执行权被另一条线程抢走,又执行addAndGet(100),这样的结果就是200,而不是我们预想的202
解决方法就是添加Synchronized关键字,同步化!
以上是关于多线程九 原子类的主要内容,如果未能解决你的问题,请参考以下文章