什么是原子量,原子量就是一次操作,要么成功,要么失败。比如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. Spring Security ——AuthenticationProvider

    AuthenticationProvider 目录 1.1     用户信息从数据库获取 1.1.1    使用jdbc-user-service获取 1.1.2    直接使用JdbcDaoImpl ...

  2. 程序猿的日常——Mybatis现学现卖

    最近有一个小项目需求,需要用spring mvc + mybatis实现一个复杂的配置系统.其中遇到了很多不太常见的问题,在这里特意记录下: 主要涉及的内容有 事务 多表删除 插入并返回主键 1 sp ...

  3. 大众点评Cat--架构分析

    转自:http://blog.csdn.net/szwandcj/article/details/51025669 Cat功能强大且多,光日志的报表和图表分析就有十几种,但文档却很少,寥寥无几找到一些 ...

  4. dubbo实现原理之动态编译

    Dubbo为了实现基于spi思想的扩展特性,特别是能够灵活添加额外功能,对于扩展或则策略选择的设配类能够动态生成.对于一些需求已知的类如Protocal,它们的设配类代码dubbo可以直接的提供,但是 ...

  5. Linux Shell命令行快捷键

    1. 命令行编辑 <Ctrl>+a 移动光标到行首 <Ctrl>+e ..........行尾 <Alt>+f 光标右移一个词 <Alt>+b .... ...

  6. JS实现网页背景旋转缩放轮播效果

    实现效果:效果预览 css代码: .switch_images { display: inline-block; margin:; padding:; width: 100%; height: 100 ...

  7. WIN10安装scrapy/channels等不成功的解决方式

    问题 在Win10机器上,不管是安装scrapy还是channels,都需要安装一个包,叫做twisted.正是这个twisted,导致出现一系列的奇葩错误,让我一度以为我的Pycharm坏了,还改了 ...

  8. Spring4 mvc+maven 框架搭建(3)

    经过前面两个环节,spring mvc的原料已经准备好了,现在就可以正式开始搭建springmvc框架了. 首先先介绍介绍搭建的框架具有的功能: 1)集成log4j,配置好日志相关并可以打印出相关的日 ...

  9. c3p0配置文件

    配置文件 名称必须为c3p0-config.xml,否则找不到: 标签名称 <c3p0-config> <default-config > 具体配置内容 </defaul ...

  10. 使用Svn的版本号[转载]

    1. 生成一个名为autover的项目 注意项目的Properties文件夹下有一个名为AssemblyInfo.cs的文件,autover程序的版本号就写在它里面. 2. 创建模板文件 在Windo ...