java 原子性int_Java多线程之原子性 volatile、atomicInteger测试

一、补充概念

1.什么是线程安全性?

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

2.Java中的“同步”

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

2.原子性

原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型)这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++;这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

二、实例源码

1 public class IncrementTestDemo {

2

3 public static int count = 0;

4 public static Counter counter = new Counter();

5 public static AtomicInteger atomicInteger = new AtomicInteger(0);

6 volatile public static int countVolatile = 0;

7

8 public static void main(String[] args) {

9 for (int i = 0; i < 10; i++) {

10 new Thread() {

11 public void run() {

12 for (int j = 0; j < 1000; j++) {

13 count++;

14 counter.increment();

15 atomicInteger.getAndIncrement();

16 countVolatile++;

17 }

18 }

19 }.start();

20 }

21 try {

22 Thread.sleep(3000);

23 } catch (InterruptedException e) {

24 e.printStackTrace();

25 }

26

27 System.out.println("static count: " + count);

28 System.out.println("Counter: " + counter.getValue());

29 System.out.println("AtomicInteger: " + atomicInteger.intValue());

30 System.out.println("countVolatile: " + countVolatile);

31 }

32

33 }

34

35 class Counter {

36 private int value;

37

38 public synchronized int getValue() {

39 return value;

40 }

41

42 public synchronized int increment() {

43 return ++value;

44 }

45

46 public synchronized int decrement() {

47 return --value;

48 }

49 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

输出结果:

+ View code

static count: 9952

Counter: 10000

AtomicInteger: 10000

countVolatile: 9979

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

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

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

三、Java中的自增原理

虽然递增操作++i是一种紧凑的语法,使其看上去只是一个操作,但这个操作并非原子的,因而它并不会作为一个不可分割的操作来执行。实际上,它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。这是一个“读取 - 修改 - 写入”的操作序列,并且其结果状态依赖于之前的状态。

下面写一个简单的类,用jdk中的工具javap来反编译Java字节码文件。

/**

* @author zhengbinMac

*/

public class TestDemo {

public static int count;

public void code() {

count++;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

localhost:Increment zhengbinMac$ javap -c TestDemo

警告: 二进制文件TestDemo包含Increment.TestDemo

Compiled from "TestDemo.java"

public class Increment.TestDemo {

public static int count;

public Increment.TestDemo();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

public void code();

Code:

0: getstatic #2 // Field count:I

3: iconst_1

4: iadd

5: putstatic #2 // Field count:I

8: return

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

如上字节码,我们发现自增操作包括取数(getstatic  #2)、加一(iconst_1和iadd)、保存(putstatic  #2),并不是我们认为的一条机器指令搞定的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值