JUC源码1-原子量
什么是原子量,原子量就是一次操作,要么成功,要么失败。比如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上实现,
虚拟机内存模型中定义的访问操作与物理计算机处理的基本一致!
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无锁操作。)
/**
直接设置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-原子量的更多相关文章
- 【JUC源码解析】ScheduledThreadPoolExecutor
简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...
- JUC源码分析-其它工具类(一)ThreadLocalRandom
JUC源码分析-其它工具类(一)ThreadLocalRandom ThreadLocalRandom 是 JDK7 在 JUC 包下新增的随机数生成器,它解决了 Random 在多线程下多个线程竞争 ...
- JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor
JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...
- JUC源码分析-线程池篇(二)FutureTask
JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...
- JUC源码分析-线程池篇(三)Timer
JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...
- JUC源码分析-线程池篇(一):ThreadPoolExecutor
JUC源码分析-线程池篇(一):ThreadPoolExecutor Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池 ...
- JUC源码分析-集合篇:并发类容器介绍
JUC源码分析-集合篇:并发类容器介绍 同步类容器是 线程安全 的,如 Vector.HashTable 等容器的同步功能都是由 Collections.synchronizedMap 等工厂方法去创 ...
- JUC源码分析-集合篇(十)LinkedTransferQueue
JUC源码分析-集合篇(十)LinkedTransferQueue LinkedTransferQueue(LTQ) 相比 BlockingQueue 更进一步,生产者会一直阻塞直到所添加到队列的元素 ...
- JUC源码分析-集合篇(九)SynchronousQueue
JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...
- JUC源码分析-集合篇(八)DelayQueue
JUC源码分析-集合篇(八)DelayQueue DelayQueue 是一个支持延时获取元素的无界阻塞队列.队列使用 PriorityQueue 来实现. 队列中的元素必须实现 Delayed 接口 ...
随机推荐
- [UWP开发] 在低版本中使用亚克力刷以及部分高版本控件
写于2018.10.3 仅在我的PC(17763)和WP(Limia 950 XL 15254)上测试过 微软在build 16299中引入了亚克力刷,但是Win10m并没有活那么久,lumia手机在 ...
- [Leetcode]120.三角形路径最小和
---恢复内容开始--- 题目的链接 简单的动态规划题,使用了二维dp数组就能很好的表示. 由于有边界的问题,所以这个dp数组为 dp[n+1][n+1]. dp[i][j]意思是终点为(i-1,j- ...
- POJ 2485
#include<iostream> #define MAXN 505 #define inf 1000000000 using namespace std; typedef int el ...
- Oracle 11g安装时针对不同操作系统所需的依赖包查询地址
http://docs.oracle.com/cd/E11882_01/install.112/e24326/ 点击连接,出现页面,往下滑动:)
- HTTP响应状态码 含义
100 客户端可以继续 101 指示服务器正根据 Upgrade 头切换协议 200 请求正常成功 201 指示请求成功并在服务器上创建了一个新资源 202 指示已接受请求进行处理但处理尚未完成 20 ...
- Dubbo--002--例子程序
虽然项目中用到了dubbo,还是想自己玩玩写个demo.百度一搜,关于dubbo的helloworld基本都是在windows上做的.现在在Linux上玩一下. 环境准备: jdk版本:1.7 服务器 ...
- 【从0到1学Web前端】CSS伪类和伪元素 分类: HTML+CSS 2015-06-02 22:29 1065人阅读 评论(0) 收藏
1.CSS中的伪类 CSS 伪类用于向某些选择器添加特殊的效果. 语法: selector : pseudo-class {property: value} CSS 类也可与伪类搭配使用 select ...
- 整理学习ASP.NET MVC的资源
网站 http://www.asp.net/mvc http://stackoverflow.com/questions/tagged/asp.net-mvc+asp.net-mvc-4?sort=n ...
- Sublime Text3 一些实用设置
字体大小 "font_size": 14 高亮编辑中的那一行 "highlight_line": true 当你把脑袋扭过到显示器以外的地方后再回头看编辑器,光 ...
- hibernate对连接池的支持和HQL查询
hibernate对连接池的支持 连接池, 作用: 管理连接:提升连接的利用效率! 常用的连接池: C3P0连接池 Hibernate 自带的也有一个连接池,且对C3P0连接池也有支持! 只维护一个连 ...