Atomic相关类和CAS机制

原子操作

public class TestCollection {
	public static void main(String[] args) throws Exception {
		final TestCollection test = new TestCollection();
		for (int i = 0; i < 60000; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					test.add();
				}
			}).start();
		}
		Thread.sleep(3000);
		System.out.println(test.i);
	}

	volatile int i=0;//使用volatile,保证了可见性

	public void add() {
		i++;
	}
}
结果:(结果不确定,<=6000059995

由于i++不是原子操作,使用 javap -v -p xxx.class 反编译,可以看i++操作的字节码指令,如下图红框中的4行:
在这里插入图片描述
i++分为3个步骤:
1、获取i的值
2、将i进行+1操作
3、把i+1得到的结果赋值到i
两个线程t1,t2,当t1线程执行步骤1后,t2线程开始执行步骤1,即:t1获取到的i=0,t2获取到的i=0,然后t1、t2进行步骤2操作,最后进行步骤3操作,导致,赋值的结果都是1。而不是我们期待的结果2。
在这里插入图片描述
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分(不可中断性)。
将整个操作视为一个整体,资源在该次操作中保持一致,这是原子性的核心特征。

可以使用synchronized关键字,reentrantLock(),atomicInteger()等等,把i++变为原子操作,实现原子操作。

CAS(Compare and swap)

Compare and swap 比较和交换。属于硬件同步原语,处理器提供了基本内存操作的原子性保证。
CAS操作需要输入两个数值,一个旧值A(期望操作前的值)一个新值B,在操作期间先对旧值进行比较,若没有发送变化,才交换成新值,发生了变化则不交换。
JAVA中的sun.misc.Unsafe类,提供了compareAndSwapInt()和compareAndSwapLong()等几个方法,实现CAS。
如果CAS操作失败了,进行自旋(for循环)

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class TestCollection {
	public static void main(String[] args) throws Exception {
		final TestCollection test = new TestCollection();
		for (int i = 0; i < 60000; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					test.add();
				}
			}).start();
		}
		Thread.sleep(6000);
		System.out.println(test.i);
	}
	
	volatile int i = 0;
	// 保存i字段的偏移量
	private static long valueOffset;
	private static Unsafe unsafe = null;

	static {
		// unsafe = Unsafe.getUnsafe();//是不行的,会报错
		// 使用反射,获取unsafe
		try {
			Field field = Unsafe.class.getDeclaredField("theUnsafe");
			field.setAccessible(true);
			unsafe = (Unsafe) field.get(null);
			
			// 获取到i的偏移量
			Field fieldi = TestCollection.class.getDeclaredField("i");
			valueOffset = unsafe.objectFieldOffset(fieldi);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void add() {
		// i++;
		for (;;) {
			// 参数:那个对象,字段偏移量。。。。获取到i对象
			int current = unsafe.getIntVolatile(this, valueOffset);
			// 参数:那个对象,那个字段,旧值,新值 。。。。。修改i对象
			if (unsafe.compareAndSwapInt(this, valueOffset, current,
					current + 1)) {
				// 修改成功,则跳出循环
				break;
			}
		}
	}
}
运行结果:
60000(每次都是60000

J.U.C包内的原子操作封装类

在这里插入图片描述

CAS的三个问题

1、循环+CAS,自旋的实现让所有线程都处于高频运行,争抢CPU执行时间的状态。如果操作长时间不成功,会带来很大的CPU资源消耗。
2、仅针对单个变量的操作,不能用于多个变量来实现原子操作。
3、ABA问题。

ABA问题

在这里插入图片描述

线程安全概念

竞态条件:如果程序运行顺序的改变会影响最终结果,就说存在竞态条件。
大多数竞态条件的本质,就是基于某种可能失效的观察结果,来做出判断或执行某个计算。
临界区:存在竞态条件的代码区域叫临界区。

共享资源

只有当多个线程更新共享资源(线程内:线程共享内存区域,线程外:DB等)时,才会发生竞态条件,可能会出现线程安全问题。
栈封闭,不会再线程之间共享的变量,都是线程安全的。
局部对象引用本身不共享,但是引用的对象存储在共享堆中。如果方法内创建的对象,只是在方法中传递,并且不对其他线程可用,那么也是线程安全的。
不可变的共享对象,保证对象在线程间共享时不会被修改,从而实现线程安全。
实例被创建,value变量就不能在被修改,这就是不可变性。

使用ThreadLocal时,相当于不同的线程操作的是不同的资源,所以不存在线程安全问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值