码迷,mamicode.com
首页 > 其他好文 > 详细

无锁机制下的原子性操作

时间:2018-01-06 14:12:21      阅读:144      评论:0      收藏:0      [点我收藏+]

标签:字段   pre   logs   cbo   http   cpu   zed   常用   date   

  通常使用volatile关键字修饰字段可以实现多个线程的可见性和读写的原子性,但是对于字段的复杂性操作就需要使用synchronize关键字来进行,例如:

public class Counter {

    private volatile int count = 0;
    
    public synchronized int getAndIncr() {
        return this.count ++;
    }
    
}

这里可以看到,对于字段的简单设置和获取,volatile可以应付,但是我们想每次获取后自增加1,这样的操作就只能交给synchronize来做,这样做虽然可以但是性能较低,所以我们这里介绍一种新的无锁操作CAS。

 

什么是CAS(Compare And Swap)

  比较并且交换内存地址的数据,由CPU在指令级别上进行操作的原子性。

CAS包含三个参数:1、变量所在的地址A   2、变量应该的值V1   3、我们需要改的值V2。

如果说变量地址A上的值为V1,就用V2进行复制;如果值不是V1,则什么都不操作,返回V1的值。

自旋操作:在一个死循环里不停的进行CAS的操作,直到成功为止。

 

CAS实现原子操作的三大问题

  1. 当线程想把地址A的值V1改为V3时,当线程取到V1的值后再进行比较时,地址A的值从V1->V2->V1进行了改变,虽然V3可以正常赋值,但是比较的V1值已经不是取出来的V1了。
  2. 循环时间很长的话,CPU的负荷较大。
  3. 对一个变量进行操作可以,同时操作多个共享的变量有些麻烦。

 

CAS线程安全

  通过硬件层面的阻塞实现原子操作的安全性。

 

常用的原子操作类

  • 更新基本类型类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • 更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 更新引用类:AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReference
  • 原子更新字段类:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedReference
public class AtomicArray {

    static int[] value = new int[]{1, 2};
    static AtomicIntegerArray aia = new AtomicIntegerArray(value);
    
    public static void main(String[] args) {
        aia.set(0, 3);
        System.out.println(aia.get(0));
        System.out.println(value[0]);
    }
}

输出:

3
1

可以看出AtomicIntegerArray对象将int数组复制了一份,他的改变并没有影响到原有数组。

public class AtomicRef {

    static class User {
        private String name;
        private int age;
        
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    static AtomicReference<User> userAtomicRef = new AtomicReference<AtomicRef.User>();
    
    public static void main(String[] args) {
        User user = new User("Mark", 25);
        userAtomicRef.set(user);
        User updUser = new User("Mark", 28);
        userAtomicRef.compareAndSet(user, updUser);
        System.out.println(userAtomicRef.get().getName());
        System.out.println(userAtomicRef.get().getAge());
    }
    
}

输出:

Mark
28

这里会首先进行比较,然后更新不一样的字段。

这里对CAS三大问题的第一个问题进行解决:

AtomicMarkableReference这个类会在数据被修改之后,标记位会由true变为false,判断这个标记位就可得知。

AtomicStampedReference这个类会在数据上添加时间戳,如果有数据修改时间戳会变掉,从而判断数据是否被修改。

 


 

本文索引关键字:

CAS:http://www.cnblogs.com/huanStephen/p/8213678.html#CAS

欢迎大家索引!

无锁机制下的原子性操作

标签:字段   pre   logs   cbo   http   cpu   zed   常用   date   

原文地址:https://www.cnblogs.com/huanStephen/p/8213678.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!