volatile不能保证原子性及其解决方案

volatile不能保证原子性及其解决方案

volatile类型的变量有三个特点
1:可见性
2:不能保证原子性
3:禁止重排

2、不能保证原子性

或者说最终一致性不能得到保证,我们看如下案例

import java.util.concurrent.TimeUnit;
class MyData{
     //我们创建一个资源类
	volatile int number = 0;
	public void numberPlusPlus() {
     
		number++;
	}
}
public class VolatileDemo {
     	
	public static void main(String[] args) {
     
		MyData myData = new MyData();	
			for (int i = 1; i <=20; i++) {
     
				new Thread(()->{
     //每次循环新建一个线程,一共创建20个线程
					for (int j = 1; j <=1000; j++) {
     
					//每一个线程都使number自加1000次
						myData.numberPlusPlus();
					}				
				},String.valueOf(i)).start();
			}
			try {
     
				TimeUnit.SECONDS.sleep(3);
				//设置3秒的时长
			} catch (Exception e) {
     
				// TODO: handle exception
			}
			while (Thread.activeCount()>2) {
     
			//判断大于两个活跃线程,是因为,一个Java程序,还有主线程和gc线程
				Thread.yield();
			}	
			System.out.println(Thread.currentThread().getName()+myData.number);
	}
}

很容易理解的一个程序,按照正常的想法来看20个线程,每个使number自加1000次
应该的到20000的结果
但是此程序实际运行结果则不然,基本运行的都不是20000(偶然情况下,也可以是,但是概率很小),这是因为多线程的情况下,各个线程抢占资源,对数据进行读写,有一部分是同时进行的看下图
volatile不能保证原子性及其解决方案_第1张图片
比如说 a线程此时拿到了number的值为0,b线程也拿到了number的值为0,他俩一起返回各自的内存工作区,进行自加操作,此时a线程中的number变为1,然后返回主内存中把主内存的number的值改写为1。b线程中的number在自己的内存工作区中,b线程把number自加为1,再去主内存中把number的值改写为1。
此时很明显,number自加了两次,却最后的值为1
这就是多线程中存在的一个问题,但是肯定万能的程序员肯定是有解决办法的
这里给大家提供两种
1:加锁synchronized(不推荐因为不是最优的办法)
public ynchronized void numberPlusPlus() {
number++;
}
2:AtomicInteger类型数据
我的理解是原子integer类,这个类型的数据可以保证各个线程操作的是同一个数据,一个线程对该数据进行操作时,其他线程不能操作,这就保证的原子性或者说最终一致性
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtomicInteger() {
atomicInteger.getAndIncrement();
//AtomicInteger类中有很多的方法
//此时调用的方法表示自加
//当然还有很多的方法,这就需要各位大牛自己研究了
}

你可能感兴趣的:(小结,volatile,原子性,AtomicInteger)