您的位置:首页 > 编程语言 > Java开发

Java并发包使用及源码浅析(原子类Atomic)

2017-03-11 16:32 423 查看

原子类(Atomic)

在并发编程中,对于一成员变量有多个线程在对它进行读写操作,如何保证它的线程安全性?java中提供synchronized块、被synchronized修饰的方法、volatile(实际上不并不是线程安全的)、以及本文将介绍的原子操作类。原子操作类有AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference<V>等


一、synchronized关键字:

synchronized大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用,这样就保证了线程的安全性,synchronized用法暂不做介绍,具体使用请移步:

http://www.jianshu.com/p/ea9a482ece5f

二、volatile关键字

1、计数器示例

有一个main线程会创建5000个线程来修改Counter类的count变量,最后打印count的值,按照预期conut的值应该是5000,可实际上并不是如此,代码如下:

public class Counter {

private volatile static int count = 0;

/**
* 增加
*/
public static void increment() {
count++;
}
/**
* 获取值
* @return
*/
public static int get() {
return count;
}

public static void main(String[] args) {

//创建5000个线程来对Counter进行增加操作
for (int i = 0; i < 5000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.increment();
}
}).start();
}
//阻塞2秒是为了防止主线程先执行完。
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("计算结果:" + Counter.get());
}
}


以下是代码运行结果:



所以volatile并不能保证count的值正确,为什么会产生这样的结果呢?

程序中申明synchronized会有两个效果该代码具有 原子性(atomicity)和 可见性(visibility)。

1、原子性-原子性意味着个时刻,只有一个线程能够执行一段代码,这段代码通过一个monitor object保护。从而防止多个线程在更新共享状态时相互冲突。

2、可见性-可见性则更为微妙,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

http://www.ibm.com/developerworks/cn/Java/j-jtp06197.html

volatile具有可见性,但无原子性,count值的写入要依赖于count当前的值,count++操作看上去就一句代码,实际上它是一个由(读取-修改-写入)操作序列组成的组合操作,而且要以原子性进行,显然volatile是满足不了的,所以volatile的使用条件必须同时满足下面两个条件:

对变量的写操作不依赖于当前值。

该变量没有包含在具有其他变量的不变式中。

三、AtomicInteger

同上面的计数器,++操作改为AtomicInteger来实现,代码如下:

public class AtomicCounter {

public  static AtomicInteger ai = new AtomicInteger(0);

/**
* 增加
*/
public static void increment() {
ai.incrementAndGet();
}
/**
* 获取值
* @return
*/
public static int get(){
return ai.get();
}

public static void main(String[] args) {

//创建5000个线程来对Counter进行增加操作
for (int i = 0; i < 5000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
AtomicCounter.increment();
}
}).start();
}

try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//阻塞2秒是为了防止主线程先执行完。
System.out.println("计算结果:" + AtomicCounter.get());
}
}


以下是代码运行结果:



要实现类似计数器的效果用AtomicInteger无疑是最好的方式之一,AtomicBoolean、AtomicLong、AtomicReference等提供多元化的原子操作, 原子操作实现原理请移步:

http://ifeve.com/atomic-operation/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: