java-CAS 原理
来源:互联网 发布:classic动作数据下载 编辑:程序博客网 时间:2024/06/10 05:49
最近感兴趣的内容到J.U.C包中了,当然了,对于这个包我的理解还是只看见大门,首先,自己没机会在项目中实战,其次,平时很少练手,再其次,最近才看,所以很多文章都是网上大牛写的,自己翻译过来,就当做逼自己重新看一遍了。
首先谈谈自己对于锁的恐惧吧,其实对于每个初级人员来说,锁是一个高深的问题,一般情况下我估计所有的初级人员来说的话,都很少使用,即使使用了,也是简单的synchronized吧,对于其他的锁我估计都很少使用,笔者最近使用了redis锁,说来其实很讽刺啊,单服务器的锁还没玩的溜,都开始装逼使用redis锁了,hh。
我对于使用synchronized的理解:
1) 当前线程持有锁的话,其他线程就挂起了。
2) 如果在有竞争的情况下呢?加锁和释放锁肯定会带来性能上的消耗吧。
3) 如果一个线程的优先级高,但是确在等待一个优先级低的线程释放锁,是否会有问题(这个只是猜测了,本人其实不知道),纯粹来源于网络知识。
两种锁:悲观锁和乐观锁。
synchronized就是一个悲观锁,会导致所有的线程等待当前线程释放锁之后才能进入。
乐观锁就是每次不加锁而是假设没有冲突去完成某项操作,如果因为冲突失败了就重试,直至成功,乐观锁的一种实现就是CAS Compare And Swap.
CAS有三个操作数----内存旧值(V),预期原值(A) 和新值(B)。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
CAS利用了CPU的CAS指令和 JNI的来完成java的非阻塞算法。
关于ABA问题不去记录了,现在已近不存在这个问题了。
具体来看AtomicInteger这个类。Unsafe这个类就是java代码和计算机底层进行交互时使用的代码。这个我不想关注,知道即可。看其中的valueOffset是什么?CAS是拿期望的值和原本的值作比较,相同的话就更新为新值,那这个valueOffset其实就是原本的值。这个方法拿到的就是一个原本的值的内存地址(为什么要看内存地址,就是去解决ABA问题的)。
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }value是volatile的,volatile变量在内存中对任意线程都是可见的,但是它不能保证原子性
private volatile int value;
来看看这个方法。内部是个无线循环,这就是上文提到的乐观锁,假设没有竞争,每个程序都是在无线循环,直至条件满足,return掉。
current和next这两个操作都是非线程安全的,关键的就在于compareAndSet这个方法。
/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }这边对于get方法来说只传入了current和next的值,但是底层计算时并不是这样,this,当前对象的地址,valueOffset的为原本值的相对对象的偏移量,expect为期望的值,update为新值,也就是expect的值和valueOffset处的值相等时,更新为新值。
/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
其他方法多多少少都是基于此,就不一一看了。
但是AtomicInteger并不能来测试啊,看不见输出啊, 看不见每个线程的旧值,新值啊。怎么办呢?自己实现一个呗。
首先要结局unsafe这个玩意儿吧,至于怎么获取,www.baidu.com,可以找到吧,好像只提供了一个获取方式,反射获取。
public class UnsafeFactory { private static Unsafe unsafe = null; static { try { Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); unsafe = (Unsafe) unsafeField.get(null); } catch (Exception e) { e.printStackTrace(); throw new Error("unsafe init error"); } } public static Unsafe get() { return unsafe; }}自己实现的,参考AtomicInteger就行了,没什么难处
public class UserAtomicInteger { private static final Unsafe unsafe = UnsafeFactory.get(); private volatile int value; private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset(UserAtomicInteger.class .getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } public UserAtomicInteger(int initialValue) { value = initialValue; } public void compareAndSwap(){ for(;;){ try{ System.out.println(Thread.currentThread()+"当前值为: "+value); int oldValue = value; int newValue = oldValue + 1; if(compareAndSet(oldValue,newValue)){ System.out.println("线程: "+ Thread.currentThread() + "赋值成功。value: "+ value+ " old : "+ oldValue + " new : "+ newValue); } }catch (Exception e){ System.out.println(e.getMessage()); } } } private boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }}
测试类,起4个线程,while一直去运行。
public class TestAtomic { public static void main(String[] args) { UserAtomicInteger a = new UserAtomicInteger(1); MyThread t1 = new MyThread(a); MyThread t2 = new MyThread(a); MyThread t3 = new MyThread(a); MyThread t4 = new MyThread(a); t1.start(); t2.start(); t3.start(); t4.start(); }}class MyThread extends Thread{ UserAtomicInteger use; public MyThread(UserAtomicInteger use){ this.use = use; } @Override public void run() { try{ while (true){ use.compareAndSwap(); } }catch (Exception e){ e.getMessage(); } }}
可以看见输出,
看见第5行和第7行,线程1读取到为3,线程3读取到也为3,但是线程1进入了CAS过程,然后值为4了,线程3虽然读到了是3,但是此时值已变,最新值是4
Thread[Thread-0,5,main]当前值为: 1线程: Thread[Thread-0,5,main]赋值成功。value: 2 old : 1 new : 2Thread[Thread-2,5,main]当前值为: 2线程: Thread[Thread-2,5,main]赋值成功。value: 3 old : 2 new : 3Thread[Thread-1,5,main]当前值为: 3线程: Thread[Thread-1,5,main]赋值成功。value: 4 old : 3 new : 4Thread[Thread-3,5,main]当前值为: 3线程: Thread[Thread-3,5,main]赋值成功。value: 5 old : 4 new : 5Thread[Thread-0,5,main]当前值为: 5线程: Thread[Thread-0,5,main]赋值成功。value: 6 old : 5 new : 6Thread[Thread-2,5,main]当前值为: 6线程: Thread[Thread-2,5,main]赋值成功。value: 7 old : 6 new : 7Thread[Thread-3,5,main]当前值为: 7线程: Thread[Thread-3,5,main]赋值成功。value: 8 old : 7 new : 8Thread[Thread-1,5,main]当前值为: 7线程: Thread[Thread-1,5,main]赋值成功。value: 9 old : 8 new : 9Thread[Thread-0,5,main]当前值为: 9线程: Thread[Thread-0,5,main]赋值成功。value: 10 old : 9 new : 10Thread[Thread-2,5,main]当前值为: 10线程: Thread[Thread-2,5,main]赋值成功。value: 11 old : 10 new : 11Thread[Thread-1,5,main]当前值为: 11线程: Thread[Thread-1,5,main]赋值成功。value: 12 old : 11 new : 12Thread[Thread-3,5,main]当前值为: 11线程: Thread[Thread-3,5,main]赋值成功。value: 13 old : 12 new : 13Thread[Thread-0,5,main]当前值为: 13线程: Thread[Thread-0,5,main]赋值成功。value: 14 old : 13 new : 14Thread[Thread-2,5,main]当前值为: 14线程: Thread[Thread-2,5,main]赋值成功。value: 15 old : 14 new : 15Thread[Thread-3,5,main]当前值为: 15线程: Thread[Thread-3,5,main]赋值成功。value: 16 old : 15 new : 16Thread[Thread-1,5,main]当前值为: 16线程: Thread[Thread-1,5,main]赋值成功。value: 17 old : 16 new : 17Thread[Thread-0,5,main]当前值为: 17线程: Thread[Thread-0,5,main]赋值成功。value: 18 old : 17 new : 18Thread[Thread-2,5,main]当前值为: 18线程: Thread[Thread-2,5,main]赋值成功。value: 19 old : 18 new : 19Thread[Thread-1,5,main]当前值为: 19线程: Thread[Thread-1,5,main]赋值成功。value: 20 old : 19 new : 20Thread[Thread-3,5,main]当前值为: 20线程: Thread[Thread-3,5,main]赋值成功。value: 21 old : 20 new : 21
- JAVA CAS 原理分析
- java CAS原理
- java CAS原理分析
- java cas原理
- JAVA中的CAS原理
- JAVA CAS原理分析
- java-CAS 原理
- JAVA CAS原理分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- JAVA CAS原理深度分析
- java CAS原理深度分析
- JAVA CAS原理深度分析
- E-MapReduce集群启停HDFS/YARN服务
- JAVA基础
- 程序10--格式化时间
- jquery如何判断滚动条滚到页面底部并执行事件
- ffmpeg编译参数(中文完整)
- java-CAS 原理
- Python基本数据类型相关练习题
- java_过滤HTML标签
- Spring @Resource、@Autowired、@Qualifier的注解注入及区别
- Java回调机制(CallBack)详解
- (原创)如何在脚本调试过程中输出调试诊断信息
- greenDao官网
- ubuntu运行sh脚本sudo自动输入密码
- 修改java类后web应用不用重启的办法