原子操作CAS

原子操作CAS

  • 今天给大家介绍原子类操作,在多线程同时更新一个变量,可能造成更新后的值不准确。synchronized会保证多线程不会同时更新一个变量,但性能相对而言较低。在Jdk1.5增加了java.util.concurrent包,这个包中的原子操作类就为我们提供了一种性能高效、线程安全地更新一个变量的方式。 看看jdk中对原子操作的定义:
    在这里插入图片描述

现在的操作系统都支持原子操作,其原理就是利用了现代处理器都支持的CAS的指令,循环这个指令,直到成功为止。

同样使用原子操作也会导致下面几个问题:

  • ABA 问题: 值由A变成B,又变回A。可以通过版本号解决
  • 开销问题:CAS操作长期不成功,cpu不断的循环
  • 只能保证一个共享变量的原子操作

jdk提供的原子类

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

下面通过几个例子的常用方法介绍我们的原子类:

1.AtomicInteger

public class UseAtomicInt {
	
	static AtomicInteger ai = new AtomicInteger(10);
	
    public static void main(String[] args) {
    	System.out.println(ai.getAndIncrement());//10(out)--->11(先获取值在自增1)
    	System.out.println(ai.incrementAndGet());//11--->12(out)(先增1在拿值)
    	System.out.println(ai.get()); //12
    }
}

2.AtomicIntegerArray

public class AtomicArray {
    static int[] value = new int[] { 1, 2 };
 
    static AtomicIntegerArray ai = new AtomicIntegerArray(value);
    
    //更改的值仅仅是原子类的值,引用对象的值不变
    public static void main(String[] args) {
    	ai.getAndSet(0, 3);// 更新对应下标下的值
    	System.out.println(ai.get(0));// 3,原子类中下标为0的值,已经被修改为3
    	System.out.println(value[0]);//1, 对象数组的值是没有变动的

    }
}

3.AtomicReference

public class UseAtomicReference {
	
	static AtomicReference<UserInfo> userRef = new AtomicReference<UserInfo>();
	
	//更改的值仅仅是原子类的值,引用对象的值不变
    public static void main(String[] args) {
        UserInfo user = new UserInfo("Mark", 16);//要修改的实体的实例
        userRef.set(user);
        UserInfo updateUser = new UserInfo("nick", 17);//要变化的新实例
        userRef.compareAndSet(user, updateUser);
        System.out.println(userRef.get().getName());//输出:nick
        System.out.println(userRef.get().getAge());//输出:17
        System.out.println(user.getName()) //输出:Mark
        System.out.println(user.getAge()); //输出:16
    }
    
    //定义一个实体类
    static class UserInfo {
        private String name;
        private int age;
        public UserInfo(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
    }
}

注:ABA问题可以由AtomicMarkableReference和AtomicStampedReference解决,AtomicMarkableReference中可以有判断没有动过boolean ,AtomicStampedReference可以通过版本号发现修改过几次。以AtomicStampedReference为例:

public class UseAtomicStampedReference {
	
	static AtomicStampedReference<String> asr = 
			new AtomicStampedReference<>("Mark",0) ;    //初始化版本号为0
	

    public static void main(String[] args) throws InterruptedException {
    	final int oldStamp = asr.getStamp();//最初的版本号
    	final String oldReferenc = asr.getReference();
    	
    	System.out.println(oldReferenc+"==========="+oldStamp);
    	
    	//执行成功的线程
    	Thread rightStampThread = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()
						+"当前变量值:"+oldReferenc+"当前版本戳:"+oldStamp+"-"
						+asr.compareAndSet(oldReferenc, oldReferenc+"Java",
								oldStamp, oldStamp+1));  //版本号自增1
				
			}
    		
    	});
    	
    	//执行失败的线程
    	Thread errorStampThread = new Thread(new Runnable() {
			@Override
			public void run() {
				String reference = asr.getReference();
				System.out.println(Thread.currentThread().getName()
						+"当前变量值:"+reference+"当前版本戳:"+asr.getStamp()+"-"
						+asr.compareAndSet(reference, reference+"C",
								oldStamp, oldStamp+1));
				
			}
    		
    	});   	
    	
    	rightStampThread.start();
    	rightStampThread.join();
    	errorStampThread.start();
    	errorStampThread.join();
    	System.out.println(asr.getReference()+"==========="+asr.getStamp());
    	
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值