一、什么是原子性

原子性是世界上最小单位,具有不可分割性。比如a=0;(a非long和double类型)这个操作是不可分割的,那么我们说这个操作是原子操作。再比如:a++;这个操作实际上是a=a+1;是可分割的,所以他不是一个原子操作。

二、原子操作的作用

非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们就称它具有原子性。java.util.concurrent.atomic包下提供了一些原子类如下:

三、理解AtomicInteger类

下面是AtomicInteger类中几个常用的方法:
可能很多人和我一样存在着这样一个疑问,在AtomicInteger中是如何实现原子性的呢?有前辈已经对AtomiInteger的源码进行了分析《对 AtomicInteger 源码 的理解》我在这里仅仅是引用一下,做个记录。先来看看源码:
	public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}

看这段代码和上面对原子性的分析,从int next  = current + 1可以看到,这里很难保证操作的原子性,重点在于compareAndSet(current, next)方法

该函数 只有两个参数,可操作的确实三个值 ,即 value ,expect, update. 他使用了 由硬件保证其原子性的指令 CAS (compare and swap)。

compareAndSet  函数保证了 比较,赋值这两步操作可以通过一个原子操作完成。

然后看整个函数, 所有代码被放到了一个循环里面, 如果compareAndSet()执行失败,则说明 在int current = get(); 以后,其他线程对value进行了更新, 于是就循环一次,重新获取当前值,直到compareAndSet()执行成功为止。

综上,getAndIncrement() 方法并不是原子操作。 只是保证了他和其他函数对 value 值得更新都是有效的。他所利用的是基于冲突检测的乐观并发策略。 可以想象,这种乐观在线程数目非常多的情况下,失败的概率会指数型增加。

四、理解volatile修饰符

在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。 
一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:

package com.codeing.snail.test;

public class TestAtomic extends Thread {

	private volatile boolean pleaseStop;

	public void run() {

		while (!pleaseStop) {
// do some stuff...
}
} public void tellMeToStop() {
pleaseStop = true;
}
}

假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。 
Volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。

volatile关键字用于声明简单类型变量,如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就不是原子级别的:

package com.codeing.snail.test;

public class TestAtomic extends Thread {
public static volatile int n = 0; public void run() {
for (int i = 0; i < 10; i++)
try {
n = n + 1;
sleep(3); // 为了使运行结果更随机,延迟3毫秒 } catch (Exception e) {
}
} public static void main(String[] args) throws Exception { Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new TestAtomic();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
threads[i].start();
for (int i = 0; i < threads.length; i++)
// 100个线程都执行完后继续
threads[i].join();
System.out.println(" n= " + TestAtomic.n);
}
}

如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用。

五、锁和Atomic的使用场景

JDK的文档中说:“设计原子类主要用作各种块,用于实现非阻塞数据结构和相关基础结构类。compareAndSet()方法不是锁定的常规替换方法。仅当对象的重要更新限于单个变量时才应用它”与锁相比,另外Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。

Android多线程研究(8)——Java中的原子性理解的更多相关文章

  1. Android多线程研究(6)——多线程之间数据隔离

    在上一篇<Android多线程研究(5)--线程之间共享数据>中对线程之间的数据共享进行了学习和研究,这一篇我们来看看怎样解决多个线程之间的数据隔离问题,什么是数据隔离呢?比方说我们如今开 ...

  2. synchronized使用及java中的原子性问题

    1.Synchronized关键字使用 class X { // 修饰非静态方法 synchronized void foo() { // 临界区 } // 修饰静态方法 synchronized s ...

  3. 谈谈我对Java中CallBack的理解

    谈谈我对Java中CallBack的理解 http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallBack是回 ...

  4. 沉淀再出发:关于java中的AQS理解

    沉淀再出发:关于java中的AQS理解 一.前言 在java中有很多锁结构都继承自AQS(AbstractQueuedSynchronizer)这个抽象类如果我们仔细了解可以发现AQS的作用是非常大的 ...

  5. Java中hashcode的理解

    Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...

  6. java中ThrealLocal的理解

    目录 java中threadlocal的理解 一.threadlocal的生命周期和ThreadLocalMap的生命周期 二.ThreadLocal的作用 三.threadlocal示例 四.Inh ...

  7. java中threadlocal的理解

    [TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...

  8. Java多线程编程(1)--Java中的线程

    一.程序.进程和线程   程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...

  9. 浅谈对java中锁的理解

    在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性.synchronized机制是给共享 ...

随机推荐

  1. Linux下使用网上银行

    Linux下使用网上银行 650) this.width=650;" onclick="window.open("http://blog.51cto.com/viewpi ...

  2. API集合开发文档

    百度翻译api https://www.cnblogs.com/DevilX5/p/7079470.html 实现QQ第三方登录.网站接入 http://blog.csdn.net/u01067894 ...

  3. BOOT项目依赖另外一个模块的包,开发的时候都正常,执行clean package打包成jar时却提示依赖的模块包不存在。

    我在service1模块里依赖了common模块,开发的时候包都能正常引用到,启动也能正常测试访问,可是奇怪的是,当我要打包成jar包时,就提示service1里依赖common的包都不存在,之前从没 ...

  4. Python实现简单的HTTP服务器(支持文件下载)

    python内置模块 SimpleHTTPServer  (支持下载功能) 在对应的工作目录下,运行命令python -m SimpleHTTPServer 即可把当前目录下以共享服务的形式共享出去. ...

  5. android 自己定义状态栏和导航栏分析与实现

    效果 android 4.4之后,系统是支持自己定义状态栏和导航栏的.举个最典型的样例就是bilibiliclient了(iOS版本号和android版本号能用两套全然不一样符合各自系统的设计ui,良 ...

  6. 33.Node.js 文件系统fs

    转自:http://www.runoob.com/nodejs/nodejs-module-system.html Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API. Node ...

  7. 团队作业-Beta冲刺(1)

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass2 这个作业要求在哪里 https://edu.cnblo ...

  8. SQL2012的新分页方法

    SELECT BusinessEntityID , FirstName , LastName FROM Person.Person ORDER BY BusinessEntityID OFFSET ( ...

  9. BZOJ2243: [SDOI2011]染色(树链剖分/LCT)

    Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如 ...

  10. 一个奇怪的Java集合问题

    int size = list.size(); Integer existIndex = -1; for (int index = 0; index < size; index++) { Pho ...