本文摘自:  

  https://blog.csdn.net/fanrenxiang/article/details/80623884

  http://ifeve.com/sun-misc-unsafe/

  https://blog.csdn.net/likailonghaha/article/details/70156858

  

一、前言

 1、为什么需要AtomicInteger原子操作类?

  对于全局变量的数值类型作 count++,若没有加 synchronized 关键字则是线程不安全的,count++ 解析为 :

  ①、读取 count 值 ②、赋值 count = count + 1;显然,这个操作不具备原子性,多线程时会出现问题,如下测试:

     public static int count = 0;

     public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
new Thread() {
public void run() {
count++;
}
}.start();
}
System.out.println("count: " + count);
}

输出的结果为count: 94,这个值不定,每次测试都可能不一样,很显然,100个线程跑++操作,结果并没有像预期的那样count: 100。

  2、要是换成volatile修饰count变量呢?

  volatile 修饰的变量能够在线程之间保持可见性,能被多个线程同时读但是又能保证只被单线程写,而且不会读到过期值,volatile 修饰字段的写入操作总是优先于读操作,即使多个线程同时修改 volatile 变量字段,总能保证获取到的是最新的值。如下测试:

     public  static  volatile  int count = 0;

     public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 100; j++) {
count++;
}
}
}.start();
}
Thread.sleep(1000);
System.out.println("volatile count: " + count); }

结果似乎又失望了,出现volatile count: 9992,果然还是出现问题了,volatile仅仅保证变量在线程间保持可见性,却依然不能保证非原子性的操作。

  3、用了AtomicInteger类后会变成什么样子呢?

public class AtomicIntegerTest2 {

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 100; j++) {
count.getAndIncrement();
}
}
}.start();
}
Thread.sleep(1000);
System.out.println("AtomicInteger count: " + count);
}
}

结果每次都输出"AtomicInteger count: 10000",没毛病。

二、AtomicInteger、Unsafe 类介绍

  1、AtomicInteger 

    来自 java.util.concurrent.atomic 包,atomic包下提供AtomicBoolean/AtomicLong/AtomicInteger三个原子更新基本类型,以AtomicInteger为例,其他两种基本类似。以下是AtomicInteger囊括的大致方法

    //给AtomicInteger设置newValue并返回加oldValue
public final int getAndSet(int newValue) //如果输入的值和期望值相等就set并返回true/false
public final boolean compareAndSet(int expect, int update) //对AtomicInteger原子的加1并返回当前自增前的value
public final int getAndIncrement() //对AtomicInteger原子的减1并返回自减之前的的value
public final int getAndDecrement() //对AtomicInteger原子的加上delta值并返加之前的value
public final int getAndAdd(int delta) //对AtomicInteger原子的加1并返回加1后的值
public final int incrementAndGet() //对AtomicInteger原子的减1并返回减1后的值
public final int decrementAndGet() //给AtomicInteger原子的加上指定的delta值并返回加后的值
public final int addAndGet(int delta)

以getAndIncrement为例看下源码

public final int getAndIncrement() {
for (;;) {  // 若 compareAndSet 为 false, 则一直循环查询
//先取出AtomicInteger的当前值
int current = get();
//对当前值加1操作
int next = current + 1;
//这里很关键,通过compareAndSet方法比较当前值有没有被其它线程修改
if (compareAndSet(current, next))
return current;
}
}

compareAndSet方法里面是调用了Unsafe类的compareAndSwapInt方法(后边讨论)

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

以下先介绍 Unsafe 类:


2、Unsafe
  作用:Unsafe类提供了硬件级别的原子操作,提高了Java对底层操作的能力。
  来自 sun.misc 包,Java最初被设计为一种安全的受控环境,但是 sun.misc.Unsafe 是 Java HotSpot 提供的操作内存和线程的"后门",提供了一些可以直接操控内存和线程的底层操作。
  Unsafe被JDK广泛应用于 java.nio 和并发包等实现中,这个不安全的类提供了一个观察 HotSpot JVM 内部结构并且可以对其进行修改,但是不建议在生产环境中使用   先看部分 Unsafe 源码:
    private Unsafe() {  // 私有
} private static final Unsafe theUnsafe = new Unsafe(); @CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}

  1、获取Unsafe实例 

  • 不能直接new Unsafe(),原因是Unsafe被设计成单例模式,构造方法是私有的;
  • 不能通过调用Unsafe.getUnsafe()获取,因为getUnsafe被设计成只能从引导类加载器(bootstrap class loader)加载

  Unsafe 实例获取方法:

//方法一:我们可以令我们的代码“受信任”。运行程序时,使用bootclasspath选项,指定系统类路径加上你使用的一个Unsafe路径(但这太难了。)
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient // 方法二: 使用反射,Unsafe类包含一个私有的、名为theUnsafe的实例,我们可以通过Java反射窃取该变量。
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe) field.get(null);
} catch (Exception e) {
}
}

  

  2、Unsafe API

  sun.misc.Unsafe类包含105个方法。实际上,对各种实体操作有几组重要方法,其中的一些如下:

Info.仅返回一些低级的内存信息

  • addressSize
  • pageSize

Objects.提供用于操作对象及其字段的方法

  • allocateInstance
  • objectFieldOffset

Classes.提供用于操作类及其静态字段的方法

  • staticFieldOffset
  • defineClass
  • defineAnonymousClass
  • ensureClassInitialized

Arrays.操作数组

  • arrayBaseOffset
  • arrayIndexScale

Synchronization.低级的同步原语

  • monitorEnter
  • tryMonitorEnter
  • monitorExit
  • compareAndSwapInt
  • putOrderedInt

Memory.直接内存访问方法

  • allocateMemory
  • copyMemory
  • freeMemory
  • getAddress
  • getInt
  • putInt

3、有趣的用例

  ①、避免初始化

   当你想要跳过对象初始化阶段,或绕过构造器的安全检查,或实例化一个没有任何公共构造器的类,allocateInstance方法是非常有用的。考虑以下类:

class A {
private long a; // not initialized value public A() {
this.a = 1; // initialization
} public long a() { return this.a; }
}

使用构造器、反射和unsafe初始化它,将得到不同的结果。

A o1 = new A(); // constructor
o1.a(); // prints 1 A o2 = A.class.newInstance(); // reflection
o2.a(); // prints 1 A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
o3.a(); // prints 0

  

Unsafe 的 allocateInstance 方法可以绕过构造方法直接创建对象

objectFieldOffset 方法可以为对象属性赋值。

    public static void main(String[] args) {
try {
Customer customer = (Customer) UNSAFE.allocateInstance(Customer.class);
customer.setName("King");
System.out.println(customer.toString()); Field name = customer.getClass().getDeclaredField("name");
UNSAFE.putObject(customer, UNSAFE.objectFieldOffset(name), "LoLa");
System.out.println(customer.toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

  ②、内存崩溃(Memory corruption)

  这对于每个C程序员来说是常见的。顺便说一下,它是绕过安全的常用技术。

  考虑下那些用于检查“访问规则”的简单类:

class Guard {
private int ACCESS_ALLOWED = 1; public boolean giveAccess() {
return 42 == ACCESS_ALLOWED;
}
}

  客户端代码是非常安全的,并且通过调用giveAccess()来检查访问规则。可惜,对于客户,它总是返回false。只有特权用户可以以某种方式改变ACCESS_ALLOWED常量的值并且得到访问

  实际上,这并不是真的。演示代码如下:

Guard guard = new Guard();
guard.giveAccess(); // false, no access // bypass
Unsafe unsafe = getUnsafe();
Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption guard.giveAccess(); // true, access granted

  ③、并发(Concurrency)

  compareAndSwap 方法是原子的,并且可用来实现高性能的、无锁的数据结构。

  比如,考虑问题:在使用大量线程的共享对象上增长值。

  

  首先,我们定义简单的Counter接口:

  

interface Counter {
void increment();
long getCounter();
}

然后,我们定义使用Counter的工作线程CounterClient

class CounterClient implements Runnable {
private Counter c;
private int num; public CounterClient(Counter c, int num) {
this.c = c;
this.num = num;
} @Override
public void run() {
for (int i = 0; i < num; i++) {
c.increment();
}
}
}

测试代码:

int NUM_OF_THREADS = 1000;
int NUM_OF_INCREMENTS = 100000;
ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREADS);
Counter counter = ... // creating instance of specific counter
long before = System.currentTimeMillis();
for (int i = 0; i < NUM_OF_THREADS; i++) {
service.submit(new CounterClient(counter, NUM_OF_INCREMENTS));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
long after = System.currentTimeMillis();
System.out.println("Counter result: " + c.getCounter());
System.out.println("Time passed in ms:" + (after - before));

第一个无锁版本的计数器:

运行快,但没有线程管理,结果是不准确的。

class StupidCounter implements Counter {
private long counter = 0; @Override
public void increment() {
counter++;
} @Override
public long getCounter() {
return counter;
}
}

输出:

Counter result: 99542945
Time passed in ms: 679

第二次尝试,添加上最简单的java式同步:

激进的同步有效,但耗时长。

class SyncCounter implements Counter {
private long counter = 0; @Override
public synchronized void increment() {
counter++;
} @Override
public long getCounter() {
return counter;
}
}

输出:

Counter result: 100000000
Time passed in ms: 10136

第三种,试试ReentrantReadWriteLock

仍然正确,耗时较短。

class LockCounter implements Counter {
private long counter = 0;
private WriteLock lock = new ReentrantReadWriteLock().writeLock(); @Override
public void increment() {
lock.lock();
counter++;
lock.unlock();
} @Override
public long getCounter() {
return counter;
}
}

第四种,atomics的运行效果如何?

AtomicCounter的运行结果更好。

class AtomicCounter implements Counter {
AtomicLong counter = new AtomicLong(0); @Override
public void increment() {
counter.incrementAndGet();
} @Override
public long getCounter() {
return counter.get();
}
}

第五种,试试Unsafe原始的compareAndSwapLong,看看它是否真的只有特权才能使用它?

class CASCounter implements Counter {
private volatile long counter = 0;
private Unsafe unsafe;
private long offset; public CASCounter() throws Exception {
unsafe = getUnsafe();
offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
} @Override
public void increment() {
long before = counter;
while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
before = counter;
}
} @Override
public long getCounter() {
return counter;
}
}

第四种、第五种时间大致一样,Unsafe 看起来似乎等价于atomics。atomics使用Unsafe?(是的)

3、 CAS

  1、CAS介绍

    AtomicInteger的核心就是一个CAS算法(CompareAndSwap),比较并交换算法,此算法是由unsafe的底层代码实现,它是一个原子的操作。 (CAS能很高效的解决原子操作)

    原理就是:如果内存中的实际值与update值相同,则将实际值更新为expect值,反之则返回失败,由上层系统循环获取实际值后,再次调用此CAS算法。

    

    CAS 是设计并发算法时常用到的一种技术,java.util.concurrent包完全建立在CAS之上。

    CAS是通过Unsafe实现的,java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁。

    看下Unsafe下的三个方法:

public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);

public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);

public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);

  

  2、CAS 在 Unsafe 中应用

  以  compareAndSwapInt 为例, CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

  unsafe.compareAndSwapInt(this, this, valueOffset, expect, update),包含 ①比较②更新两个操作,compareAndSwapInt如何这两个步骤的原子性呢?

  

  3、CAS原理

   CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用(用 native 关键字标注),允许java调用其他语言。

   而compareAndSwapInt就是借助C来调用CPU底层指令实现的。

三、总结

  这是我认真看了一天博客摘抄下来的整理,我要保持每天不断的学习,争取早日写出有水平的干货

1. AtomicInteger 、Unsafe 及 CAS方法的整理的更多相关文章

  1. JDK 1.8 sun.misc.Unsafe类CAS底层实现

    在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足. 在sun. ...

  2. Unsafe与CAS

    Unsafe 简单讲一下这个类.Java无法直接访问底层操作系统,而是通过本地(native)方法来访问.不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作 ...

  3. 随意看看AtomicInteger类和CAS

    最近在读jdk源码,怎么说呢?感觉收获还行,比看框架源码舒服多了,一些以前就感觉很模糊的概念和一些类的用法也清楚了好多,举个很简单的例子,我在读Integer类的时候,发现了原来这个类自带缓存,看看如 ...

  4. Java AtomicInteger类的使用方法详解_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Samp ...

  5. Unsafe中CAS的实现

    前言 Unsafe 是位于 sun.misc 包下的一个类.Unsafe 提供的 API 大致可分为内存操作.CAS.Class 相关.对象操作.线程调度.系统信息获取.内存屏障.数组操作等几类.由于 ...

  6. IIS6.0 IIS7.5应用程序池自动停止的解决方法 搜集整理

    来源:http://www.guchengnet.com/1499.html IIS6.0 IIS7.5应用程序池自动停止的解决方法 搜集整理 发表于2016年12月14日 有2.3个月没有用本地的i ...

  7. CentOS yum源设定使用方法的整理(转)

    CentOS yum更新了很多版本更新,我本人认为CentOS yum很好使的文件系统,在此向大家推荐CentOS应该是做为服务器的linux的佼佼者.CentOS采用的二进制包是rpm,不过包的依赖 ...

  8. 编程中遇到的Python错误和解决方法汇总整理

    这篇文章主要介绍了自己编程中遇到的Python错误和解决方法汇总整理,本文收集整理了较多的案例,需要的朋友可以参考下   开个贴,用于记录平时经常碰到的Python的错误同时对导致错误的原因进行分析, ...

  9. 最快安装AndroidStudio的方法(小歪整理)

    最快安装AndroidStudio的方法(小歪整理)-干货,加速加载和解决无法预览布局文件的等问题 最快安装AndroidStudio的方法(小歪整理) 1.使用解压压缩包的方式安装:android- ...

随机推荐

  1. oracle 11g 卸载 安装10g 成功(卸载oracle+清除注册表信息+清除oracle app文件)

    停用oracle服务:进入计算机管理,在服务中,找到oracle开头的所有服务,右击选择停止 2 在开始菜单中,找到Universal Installer,运行Oracle Universal Ins ...

  2. 如何在Web页面里使用高拍仪扫描上传图像

    如何在Web页面里使用高拍仪扫描上传图像 市场上所有的高拍仪都支持扫描图片并保存到本地,一般公司都会提供控件.开发人员只需要在页面集成就可以进行拍照和扫描.只不过一般扫描的图片是保存在本地固定的文件夹 ...

  3. Leetcode 6. ZigZag Conversion(找规律,水题)

    6. ZigZag Conversion Medium The string "PAYPALISHIRING" is written in a zigzag pattern on ...

  4. RESTful风格编程

    参考文档:http://blog.didispace.com/springbootrestfulapi/ https://www.jianshu.com/p/91600da4df95 *)RESTfu ...

  5. 接上SQL SERVER的锁机制(一)——概述(锁的种类与范围)

    二.完整的锁兼容性矩阵(见下图) 对上图的是代码说明:见下图. 三.下表列出了数据库引擎可以锁定的资源. 名称 资源 缩写 编码 呈现锁定时,描述该资源的方式 说明 数据行 RID RID 9 文件编 ...

  6. URL里的分号';'一定要编码为%3b!!!!

    http://en.wikipedia.org/wiki/Query_string The series of pairs is separated by the ampersand, '&' ...

  7. EDM营销应注意要定期发送邮件

    一个成熟的EDM营销方案应该要确定完整的邮件发送频率,并且严格按照计划执行.这点在EDM营销过程中非常重要,下面为大家分析一下. 一个EDM营销应该确定一下发送的时间,每月或者每周发送一次,这样用户能 ...

  8. flex embed 使用

    Flex 软件中经常需要使用一些外部的资源,如图片.声音.SWF或字体,虽然你也可以在软件运行的时候引入和载入,但是也可能经常需要直接将这些资源编译(Compile)到软件中,也就是直接嵌入资源(Em ...

  9. 【GIS数据处理】 利用空间关系建立线CAD数据和属性表关联

    这两天遇到一个不太容易解决的问题. 某燃气公司想自己对自建管线进行测绘便于数字化管理,在接受了简单的RTK测量培训和Cass成图培训后,就自己着手开干. 最近数据整理的差不多了,就提交给我请我帮忙核查 ...

  10. [USACO 2008 Jan. Silver]架设电话线 —— 最短路+二分

    一道图论的最短路题.一开始连最短路都没想到,可能是做的题太少了吧,完全没有思路. 题目大意: FJ的农场周围分布着N根电话线杆,任意两根电话线杆间都没有电话线相连.一共P对电话线杆间可以拉电话线,第i ...