对象的共享——可见性

可见性

单线程,写入读取,很自然。
多线程,当一个线程写入数据,一个读取时候,会产生一系列问题,无法确保读操作的线程看到其他线程写入的数据,因此,需要使用同步。

public classNovisibility{
    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或者无法终止,,因为可能读线程一直被调度,一直循环。也可能读线程先看到了read 的值,但是number的赋值在编译的时候执行了重排序(详见Java内存模型),在ready赋值之后执行,导致此时number为0被输出。

避免方法 只要有数据再多个线程之间共享,就要使用正确的同步。

失效数据

典型的get/set方法在不同步的情况下就比容易发生。

非原子的64位操作

Java内存模型中对于非volatile的long和double变量,JVM允许将64位的读操作或写操作分解为两个32位的操作,当这两种变量在不同的线程中执行时候,可能会读取到某个高32位和另一个低32位,因此,即使不考虑失效数据,多线程使用共享且可变的long和double等类型也是不安全的,除非用volatile或者加锁。

加锁与可见性

内置锁可以用于确保某个线程查看另一个线程的执行结果。当线程A执行某个同步代码块时候,线程B进入同一个锁保护的 同步代码块。可以保证,锁释放之前,B可以看到A在同一个同步代码块的所有执行结果,保证了多线程的可见性。

加锁的含义不仅仅在于局限于互斥行为,还包括内存可见性。为了所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程必须在同一个锁上同步。

Volatile变量

稍弱的同步机制,将对象的更新操作通知其他线程,编译器和运行时不会进行重排序,不会被缓存在寄存器或者其他处理器不可见的地方,因此读取volatile变量总会返回最新写入的值。

仅当volatile变量能简化代码的实现以及对同步策略的验证时候,才应该使用。如果验证正确性时需要对可见性进行复杂的判断,那么就不要使用volatile变量。正确使用方式:确保自身状态的可见性,确保引用状态的可见性,以及标识一些重要生命周期时间的发生(初始化或者关闭)

例如

volatile boolean asleep;
...
    while( !asleep ){
        couneSomeSheep();

注意 volatile不足以保证递增操作,i++

加锁机制保证可见性,原子性,volatile只保证可见性。

使用条件
  • 写入操作不依赖当前值,或保证单线程写入
  • 访问变量不需要加锁。
  • 与其他变量均属于不变性条件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值