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++) {
                        myData.numberPlusPlus();
              //每一个线程都使number自加1000次 } },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(偶然情况下,也可以是,但是概率很小),这是因为多线程的情况下,各个线程抢占资源,对数据进行读写,有一部分是同时进行的看下图

比如说 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类中有很多的方法
//此时调用的方法表示自加
//当然还有很多的方法,这就需要各位大牛自己研究了
}

猜你喜欢

转载自www.cnblogs.com/liudongshuai/p/11728716.html