什么是原子量,原子量就是一次操作,要么成功,要么失败。比如java中的i++ 或i-- , 不具备原子性,每次读取的值都是不一样的,探究其原因,x86体系中,他的总线是32位的,i++的操作指令必须是分为2步实现,那是因为,为了确保原子性,jdk在atomic-AtomicXXX 类中,通过CAS来确保原子性。对原子量的读取可以读到最新,由volatile关键字来保证可见性。

对比一下下面的代码实现:

public class Incr {  

    public AtomicInteger a = new AtomicInteger(0);  

    public int incrAtomic(){
return a.getAndIncrement();
} public int getAtomic(){
return a.get();
} public int b = 0; public int incrInt(){
return b++;
} public int getInt(){
return b;
}
}

  

public class MultiThread {  

    private static Incr incr = new Incr();  

    public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(1); for (int i = 0; i < 100; i++) {
new Thread(new Runnable() { @Override
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int j = 0; j < 100; j++) {
incr.incrAtomic();
incr.incrInt();
}
}
}).start();
} countDownLatch.countDown(); try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("AtomicInteger.incr: "+incr.getAtomic());
System.out.println("int.incr: " + incr.getInt());
}
}

  

接着,我们来看看,JUC中的atomicInteger类是如何实现CAS的?

查看源码,我们可以清晰的看到作者就是大名顶顶的 Doug Lee , 在代码的提示中,实现都是通过CAS(comparAndSwapInt)来实现数据的更新的,的注释归注释,但还是要看看,下面的具体实现,一看果然,有很多类似于下面的那样的代码,出现。

为了一探究竟,先去看看 unsafe 的实现。

查看Unsafe 类的时候,发现他的大多数实现,都是 native 属性,也就是说,他把实现都留在了JVM上实现,

在openJdk代码中可以找到这个类,目录openJdk的jdk/share/classes/sun/misc/。
       这个类里面大多数方法都是native的,方法实现可以在openJdk的hotspot/share/vm/prims/unsafe.cpp里面找到。
c的实现, 就是(多核下带lock前缀的)cmpxchgq命令了。putOrderedObject方法按之前几篇的查找方法,会发现内联之后,相当于一个普通写操作了。
具体可以参考这篇文章:http://brokendreams.iteye.com/blog/2250109
要看懂,必须了解JVM的内存模型:

虚拟机内存模型中定义的访问操作与物理计算机处理的基本一致!

Java中volatile关键字原义是“不稳定、变化”的意思 。使用volatile和不使用volatile的区别在于JVM内存主存和线程工作内存的同步之上。volatile保证变量在线程工作内存和主存之间一致。

其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我. 也就是MainMomery 中,而不是缓存中。这里的volatile只是解决了存储i值得问题,至于获取和修改i值,确是没有做到同步。so , 有了volatile 并不意味着你万事大吉了,他也会很容易产生脏数据。

1:为什么会产生错误的数据? 
多线程引起的,因为对于多线程同时操作一个整型变量在大并发操作的情况下无法做到同步,而Atom提供了很多针对此类线程安全问题的解决方案,因此解决了同时读写操作的问题。

2:为什么会造成同步问题? 
Java多线程在对变量进行操作的时候,实际上是每个线程会单独分配一个针对i值的拷贝(独立内存区域),但是申明的i值确是在主内存区域中,当对i值修改完毕后,线程会将自己内存区域块中的i值拷贝到主内存区域中,因此有可能每个线程拿到的i值是不一样的,从而出现了同步问题。

3:为什么使用volatile修饰integer变量后,还是不行? 
因为volatile仅仅只是解决了存储的问题,即i值只是保留在了一个内存区域中,但是i++这个操作,涉及到获取i值、修改i值、存储i值(i=i+1),这里的volatile只是解决了存储i值得问题,至于获取和修改i值,确是没有做到同步。

4:既然不能做到同步,那为什么还要用volatile这种修饰符? 
主要的一个原因是方便,因为只需添加一个修饰符即可,而无需做对象加锁、解锁这么麻烦的操作。但是本人不推荐使用这种机制,因为比较容易出问题(脏数据),而且也保证不了同步。

5:那到底如何解决这样的问题? 
第一种:采用同步synchronized解决,这样虽然解决了问题,但是也降低了系统的性能。 
第二种:采用原子性数据Atomic变量,这是从JDK1.5开始才存在的针对原子性的解决方案,这种方案也是目前比较好的解决方案了。(推荐,cas无锁操作。)

 就是(多核下带lock前缀的)cmpxchgq命令了。
       putOrderedObject方法按之前几篇的查找方法,会发现内联之后,相当于一个普通写操作了。
cas 代码中都会出现大量的:
for(;;){
}
操作!并意味者,他是在不断的请求锁。

/**
直接设置volatile变量的值
*/
public final void set(int newValue) {
value = newValue;
} /**
putOrderedInt,去掉了storeLoad内存屏障,只保证最终设置成功,不保证多处理环境下,其他处理器read到最新的值
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
loop循环,不断重试,直到成功
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
/**
Atomic中n多方法通过loop来调用这个方法,类似乐观锁,expect表示期望的值,update是更新的值
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
代码跟compareAndSet没什么区别,
注释里面May fail spuriously and does not provide ordering guarantees,会导致伪失败,不保证指令有序
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

  

JUC源码1-原子量的更多相关文章

  1. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  2. JUC源码分析-其它工具类(一)ThreadLocalRandom

    JUC源码分析-其它工具类(一)ThreadLocalRandom ThreadLocalRandom 是 JDK7 在 JUC 包下新增的随机数生成器,它解决了 Random 在多线程下多个线程竞争 ...

  3. JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor

    JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...

  4. JUC源码分析-线程池篇(二)FutureTask

    JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...

  5. JUC源码分析-线程池篇(三)Timer

    JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...

  6. JUC源码分析-线程池篇(一):ThreadPoolExecutor

    JUC源码分析-线程池篇(一):ThreadPoolExecutor Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池 ...

  7. JUC源码分析-集合篇:并发类容器介绍

    JUC源码分析-集合篇:并发类容器介绍 同步类容器是 线程安全 的,如 Vector.HashTable 等容器的同步功能都是由 Collections.synchronizedMap 等工厂方法去创 ...

  8. JUC源码分析-集合篇(十)LinkedTransferQueue

    JUC源码分析-集合篇(十)LinkedTransferQueue LinkedTransferQueue(LTQ) 相比 BlockingQueue 更进一步,生产者会一直阻塞直到所添加到队列的元素 ...

  9. JUC源码分析-集合篇(九)SynchronousQueue

    JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...

  10. JUC源码分析-集合篇(八)DelayQueue

    JUC源码分析-集合篇(八)DelayQueue DelayQueue 是一个支持延时获取元素的无界阻塞队列.队列使用 PriorityQueue 来实现. 队列中的元素必须实现 Delayed 接口 ...

随机推荐

  1. Azure DevOps Server:Git权限设置

    Azure DevOps Server 权限概述 在Azure DevOps Server (之前名称为TFS)中,权限是一个比较复杂的概念.从权限层级上来说,包括服务器级别.团队项目集合级别.团队项 ...

  2. html不规则表格设计

    <table border="1px" style="border-collapse:collapse;"> <tbody> <t ...

  3. UWP Background过渡动画

    首先说两件事: 1.大爆炸我还记着呢,先欠着吧... 2.博客搬家啦,新地址:https://blog.ultrabluefire.cn/ ==========下面是正文========== 前些日子 ...

  4. 最小割(zjoi2011,bzoj2229)(最小割树)

    小白在图论课上学到了一个新的概念--最小割,下课后小白在笔记本上写下了如下这段话: "对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点\(s,t\)不在同一个部分中,则称 ...

  5. spring cloud学习(四) 动态路由

    Zuul的主要功能是路由和过滤器.路由功能是微服务的一部分,zuul实现了负载均衡. 1.1 新建模块zuul pom.xml <?xml version="1.0" enc ...

  6. [ActionScript 3.0] as3处理xml的功能和遍历节点

    as3比as2处理xml的功能增强了N倍,获取或遍历节点非常之方便,类似于json对像的处理方式. XML 的一个强大功能是它能够通过文本字符的线性字符串提供复杂的嵌套数据.将数据加载到 XML 对象 ...

  7. 转---谈谈HTTP协议中的短轮询、长轮询、长连接和短连接

    作者:伯乐在线专栏作者 - 左潇龙 http://web.jobbole.com/85541/ 如有好文章投稿,请点击 → 这里了解详情 引言 最近刚到公司不到一个月,正处于熟悉项目和源码的阶段,因此 ...

  8. Swift5 语言参考(三) 类型

    在Swift中,有两种类型:命名类型和复合类型.一个名为类型是当它的定义可以给出一个特定名称的类型.命名类型包括类,结构,枚举和协议.例如,名为的用户定义类的实例MyClass具有该类型MyClass ...

  9. 机器学习基石笔记:15 Validation

    一.模型选择问题 如何选择? 视觉上 NO 不是所有资料都能可视化;人脑模型复杂度也得算上. 通过Ein NO 容易过拟合;泛化能力差. 通过Etest NO 能保证好的泛化,不过往往没法提前获得测试 ...

  10. linux服务器的相关信息查看(端口占用,cpu、内存占用,防火墙,系统信息,vim编辑器使用等)

    一.端口占用情况   https://www.cnblogs.com/CEO-H/p/7794306.html (1)查看所有端口.进程的使用情况:netstat -tunlp (2)查看某一端口的使 ...