美文网首页
Java自增原子性问题(测试Volatile、AtomicInt

Java自增原子性问题(测试Volatile、AtomicInt

作者: 错位的季节 | 来源:发表于2017-10-12 11:10 被阅读100次

一、补充概念

1.什么是线程安全性?

《Java Concurrency in Practice》中有提到:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

2.Java中的“同步”

Java中的主要同步机制是关键字“synchronized”,它提供了一种独占的加锁方式,但“同步”这个术语还包括volatile类型的变量,显式锁(Explicit Lock)以及原子变量。

3.原子性
 原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型)这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++;这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存,这样两个不同的线程在访问同一个共享变量的值时,始终看到的是同一个值。
代码示例:

 
public class IncrementTestDemo {

    public static int count = 0;
    public static Counter counter = new Counter();
    public static AtomicInteger atomicInteger = new AtomicInteger(0);
    volatile public static int countVolatile = 0;
    
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread() {
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        count++;
                        counter.increment();
                        atomicInteger.getAndIncrement();
                        countVolatile++;
                    }
                }
            }.start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("static count: " + count);
        System.out.println("Counter: " + counter.getValue());
        System.out.println("AtomicInteger: " + atomicInteger.intValue());
        System.out.println("countVolatile: " + countVolatile);
    }
    
}

class Counter {
    private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int increment() {
        return ++value;
    }

    public synchronized int decrement() {
        return --value;
    }
}

输出结果

static count: 9952
Counter: 10000
AtomicInteger: 10000
countVolatile: 9979

第一行与最后一行,每次运行将得到不同的结果,但是中间两行的结果相同。

通过上面的例子说明,要解决自增操作在多线程环境下线程不安全的问题,可以选择使用Java提供的原子类,或者使用synchronized同步方法。

而通过Volatile关键字,并不能解决非原子操作的线程安全性。

java语言规范指出:为了获取最佳的运行速度,允许线程保留共享变量的副本,当这个线程进入或者离开同步代码块时,才与共享成员变量进行比对,如果有变化再更新共享成员变量。这样当多个线程同时访问一个共享变量时,可能会存在值不同步的现象。

而volatile这个值的作用就是告诉VM:对于这个成员变量不能保存它的副本,要直接与共享成员变量交互。
建议:当多个线程同时访问一个共享变量时,可以使用volatile,而当访问的变量已在synchronized代码块中时,不必使用。
缺点:使用volatile将使得VM优化失去作用,导致效率较低,所以要在必要的时候使用。

相关文章

  • Java自增原子性问题(测试Volatile、AtomicInt

    一、补充概念 1.什么是线程安全性? 《Java Concurrency in Practice》中有提到:当多个...

  • Java--多线程锁机制

    上次通过三个例子,了解了Java并发三个特性,也分析了volatile不能解决原子性问题的原因,要解决原子性问题,...

  • Java - 原子操作类

    Java中的12个原子操作类 原子更新基本类型类 AtomicBoolean:原子更新布尔类型 AtomicInt...

  • 20-08-18-原子操作-自增操作

    JAVA学习记录-原子操作-自增操作

  • java atomic

    java atomic 原子变量提供各种原子操作,多线程场景下操作不需要加锁,性能非常好 简例 AtomicInt...

  • Volatile理解

    Java Volatile1. volatile 理解2. volatile 不保证原子性3. Volatile ...

  • java8_原子更新

    标签:java 原子更新 回顾使用 Aotimic使用(自增,自减都为原子操作) LongAdder,LongAc...

  • 原子性、可见性、有序性

      摘自Java并发编程:volatile关键字解析   在并发编程中,我们通常会遇到以下三个问题:原子性问题,可...

  • volatile理解

    volatile主要用来解决线程的可见性,有序型和原子性问题(synchronized和锁也可,volatile更...

  • volatile关键字的原理及适用场景(摘选)

    一、volatile的作用 在《Java并发编程:核心理论》一文中,我们已经提到过可见性、有序性及原子性问题,通常...

网友评论

      本文标题:Java自增原子性问题(测试Volatile、AtomicInt

      本文链接:https://www.haomeiwen.com/subject/tgdnyxtx.html