可见性和volatile -- Java并发

 

可见性是Java虚拟机上定义的逻辑上的偏序关系。通常指某些操作的结果对后续的其他操作必须是可见的。详细内容参见《Java Concurrency In Practice》,这部分内容通常被称为Java Memory Model,与操作系统Memory Model概念类似。主要通过一个简单程序说明一下,相当于一个学习总结。下面是程序

参考Doug Lea的JSR133 CookBook  http://gee.cs.oswego.edu/dl/jmm/cookbook.html

 

 

/**
 *Not Thread Safe
 */
public class Counter {

    private int count = 0;

    public void increment() {
        count++;
    }

    public int get() {
        return count;
    }
}

 程序很简单,但不是线程安全的问题很多。包括reorder,可见性,还有最基本的互斥执行,都没得到保证

 

 1. increment方法不是原子操作,自增实际上对应多个机器指令。所以并发情况下,会出现不正确结果。

那加上synchronized又如何。

 

 

 

public class Counter {

    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int get() {
        return count;
    }
}

 2.我知道肯定有人会说get执行时没有加锁,读操作也需要加锁,确实如此,注意这一点,synchronized的操作造成的结果对其他执行相同synchronized操作的线程是可见的,但由于get没有加锁,所以不能保证increment对该get操作可见。假设count是0执行了一次increment之后get到的可能还是0,因为increment的操作造成的结果也许get线程还不能看到。此外还有reordering造成的问题,但是这种情形出现几率很小。有兴趣请去看JSR 133 http://gee.cs.oswego.edu/dl/jmm/cookbook.html

现在看最后一个版本(抱歉不是都加synchronized版本)

 

 

 

public class Counter {

    private volatile int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int get() {
        return count;
    }
}

 3.根据brain geotz 同学的说法,上面的代码是线程安全的。

实际上它确实是线程安全的。因为volatile同样有可见性保证,这保证increment操作的结果对所有count的读取操作都是可见的。同样synchronized和volatile在虚拟机上都有reorder的约束,所以可以保证是线程安全的。关于更多的可见性和reorder同样参考JSR133http://gee.cs.oswego.edu/dl/jmm/cookbook.html

 

注意,这样的客户端调用是不正确的。

 class Wrapper{
        private Counter counter = new Counter();
        
        public int incrementAndGet(){
            counter.increment();
            return counter.get();
        }
    }
 Counter只保证单个get和increment操作是正确,但组合操作是不能保证线程安全的。

你可能感兴趣的:(java,thread,html)