线程安全性-volatile

[b]引[/b]
今晚读了新同事关于P2P做种的Java 代码,Code风格很漂亮,但仔细多看几眼,就会出现瑕疵。其中有一点关于线程的安全性,还是觉得有必要拿出来说说。

代码的意图是利用一个标识性Boolean变量来控制一个线程的开启(Service)与结束(Shutdown)。
A类:
private Boolean isStarted = false;

public void start () {
isStarted = true;
while(isStarted){
// do something
}
}

public void stop() {
isStarted = false;
}


看完上段代码,不少朋友应该知道问题出现在了什么地方。没错,A类是线程不安全的,其他线程对isStarted 变量的修改,对于A线程类来说,会发生不可预知的问题。

[b]对象的可见性[/b]
在一个单线程化的环境里,如果向一个变量先写入值,然后在没有写干涉的情况下读取这个变量,那么会得到预料之中的相同返回值。但是当读和写发生在不同的线程中时,通常不能保证读线程及时地读取其他线程写入的值,甚至可以说根本不可能。为了确保跨线程写入的内存可见性,需使用同步机制。
private static boolean ready;
private static int number;

private static class ReaderThread extends Thread
{
public void run() {
while(!ready)
Thread.yield();
System.out.println(number);
}
}

public static void main(String[] args){
new ReaderThread().start();
number = 42;
ready = true;
}


上段代码看起来最终执行的结果会是输出42,但事实上,它很有可能会输出0,甚至永远不会终止。这是因为没有使用恰当的同步机制。也就是说没能保证number和ready 的值对读线程是可见的。

很奇怪吧,但事实确实如此,因为对于读线程来说,ready 的值可能永远不可见,甚至会输出0,因为在对number赋值之前,主线程就已经写入ready,并对读线程写见。Okay,这是一种“重排序”(在单线程中,只要重排序不会对结果产生影响,那就不能完全保证其中的操作一定是按照程序所写的那样。 这是JVM的编译器为了充分利用多核处理器性能而设计的,这方面不了解的朋友可以了解一下Java存储模型)。

这种情况在我的项目中发生,是非常的可怕的。幸运的是被发现了,且Java提供了一个简单的方法来避免这个问题。
[b]
Volatile变量[/b]
Java提供了一种同步的弱形式(这也是一种以锁的形式出现的):volatile变量。它确保对一个变量的更新以可预见的方式通知其他的线程。当一个域声明为volatile类型后,编译器和运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。

volatile boolean asleep;
...
while (!asleep)
{
countSomeSheep();
}

一个典型的volatile变量应用:检查状态标记,以确定是否退出一个循环。为了保证执行检查的线程能够注意到asleep已被其他线程修改。Asleep标记就必须为volatile。Okay, 这里同样可以使用锁来代替volatile,一样能保证对asleep变量修改的可见性。

最后说说volatile与锁的区别:
加锁可以保证可见性与原子性,然而volatile变量只能保证可见性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值