博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发之CAS
阅读量:6387 次
发布时间:2019-06-23

本文共 2622 字,大约阅读时间需要 8 分钟。

在并发的世界里,有两种心态--乐观和悲观。乐观的人总认为这事不一定发生,侥幸地先试一把再说。悲观的人认为,哦,这件事可能会有这样那样的负面,不能轻易尝试。这些对应到我们java的并发策略就加锁和无锁。

啥?并发还有无锁这种操作?真有,而且效率相当地高。为什么效率高呢?因为加锁,意味着就有获取锁,释放锁,等待锁,阻塞。。这些,会带来线程的切换,只要线程一切换,就会要保存现场,这样的操作都是代价昂贵的,我们不希望这样的情况发生(或者尽量避免)。
本文就分析在无锁情况下,如何保证并发的安全。答案就是cas.
先看一个demo。

public class AtomicIntegerDemo {        static AtomicInteger i = new AtomicInteger();        public static class AddThread implements Runnable{            @Override            public void run() {                for (int j = 0; j < 10000; j++) {                    i.incrementAndGet();                }            }        }        public static void main(String[]s) throws InterruptedException{                Thread[] t= new Thread[10];            for (int j = 0; j < 10; j++) {                t[j]= new Thread(new AddThread());            }            for (int j = 0; j < 10; j++) {                t[j].start();            }            for (int j = 0; j < 10; j++) {                t[j].join();            }            System.out.println(i);//结果应该是10000*10        }}复制代码

这是为啥呢?一般有10个线程去执行一个i++肯定总数会小于10000*10的。 带着这个好奇我们先说下cas的铺垫,再看源码。

cas的基本思想如下:
cas(当前值, 期望值,目标值)。当执行这个cas()这个方法时,方法内部会判断当前值是否等于期望值,若等,就把当前值设为目标值,返回true。不等,返回false(说明有别的线程更改了当前值)。那咋办呢?此时一般会有个for(;;)死循环,重试,直到返回ture。为什么可以重试,上面说了这里线程没锁,当前线程有了执行权,只要没被cpu调度,就一直拥有执行权,就一直再执行for循环。现在cpu已经保证了cas这个比较并交换指令是原子性的了。。也就是说这个cas()方法嗖的一下就执行完了,不会被打断。
现在看下incrementAndGet()的实现。

public final int incrementAndGet(){    for(;;){        int current = get();//返回当前值        int next = current+1;        if(compareAndSet(current,next)){            return next;        }    }}复制代码

啊,手欠,还是想看看compareAndSet(current,next)的实现,不然总感觉不踏实,对就是这种感觉,不知道读这篇文章的你,有没这种感觉。

public final boolean compareAndSet(int expect ,int update){return unsafe.compareAndSwapInt(this,valueOffset,expext,update);}复制代码

这里出现一个我们不熟悉的变量unsafe,它是sun.misc.Unsafe类型。我猜测这个类应该是封装了些不安全的操作。啥是不安全的呢?指针!因为指针如果指错了位置,就可能覆盖了别人的内存,导致系统崩溃。Java中屏蔽了这一玩意,当有时还必须请出这玩意,这里就是一个例子。

继续进入unsafe.compareAndSwapInt()
public final native boolean compareAndSwapInt(Object o, long offset, int expect, int x); 这个方法是native的,我们应用层面是调不到的。这个方法内部就是使用了上面的cas()思想,原子指令完成。
上面的unsafe是通过这个方法获得的

public static Unsafe getUnsafe() {        Class var0 = Reflection.getCallerClass();        if((var0.getClassLoader() !=null) {            throw new SecurityException("Unsafe");        } else {            return theUnsafe;        }    }复制代码

上面的代码会检查调用getUnsafe()的类的classLoader是否为null。这就使得我们的应用程序无法使用Unsafe类。

补充一下类加载器知识。
根据Java类加载原理,应用程序由AppLoader加载。而系统核型类由Bootstrap加载。Bootstrap加载器没java对象的对象,因此获得这个类的加载器为null,所以,当一个类的加载器为null时,说明它是由Bootstrap加载的。

转载于:https://juejin.im/post/5b1be099e51d4506a521a199

你可能感兴趣的文章
IP地址分类
查看>>
eclipse启动出错的解决方案:org.osgi.framework.BundleExcep...
查看>>
纠结的名字 - 自动生成apk文件名
查看>>
vs编译protobuf 3.0.0
查看>>
话里话外:简单看流程
查看>>
话里话外:按单制造(MTO II)企业的资源瓶颈是怎么形成的?
查看>>
ext3grep practice record
查看>>
mysql利用CPU多核
查看>>
nginx strip模块优化页面
查看>>
需求管理(3)------>方法论
查看>>
mongodb 备份和恢复
查看>>
常州IBMV3700数据恢复成功
查看>>
Hibernate二级缓存与查询缓存的组合探究
查看>>
Java 理论与实践: 非阻塞算法简介
查看>>
函数和闭包之头等函数
查看>>
三: cocos2d-x代码分析
查看>>
转-linux系统脚本 环境变量 的启动顺序
查看>>
我的友情链接
查看>>
My blog please navigate to http://hi.baidu.com/248828412
查看>>
spring in action 4 第5章
查看>>