解读【Java theory and practice: Managing volatility】

来源:互联网 发布:戴尔软件下载中心 编辑:程序博客网 时间:2024/05/16 07:26
原文链接:Java theory and practice: Managing volatility

要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
对变量的写操作不依赖于当前值。
该变量没有包含在具有其他变量的不等式中。
即假设volatile变量为n,那么不应有n++,n>m等用法。

volatile的错误用法1

public class VolatileTest {    public static volatile int count = 0;    public static void main(String[] args) {        // 同时启动1000个线程,去进行i++计算,看看实际结果        Thread threads[] = new Thread[1000];        for (int i = 0; i < 1000; i++) {            threads[i] = new Thread(new Runnable() {                @Override                public void run() {                    // 这里延迟1毫秒,使得结果明显                    try {                        Thread.sleep(1);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    count++;                    //increase();                }            });            threads[i].start();        }        // 等待所有线程结束        try {            for (int i = 0; i < 1000; i++) {                threads[i].join();            }        } catch (InterruptedException e) {            e.printStackTrace();        }        // 这里每次运行的值都有可能不同,可能为1000        System.out.println("运行结果:count=" + count);    }        public static synchronized void increase() {        // 进入和退出synchronized块或方法时,会同步线程的临时变量与内存主变量。保证count的值的正确性。        count++;    }}

上述代码运行时,我们期望的结果是1000,可惜实际运行时结果并不一定是1000。
正确的做法是将count++替换成同步方法increase。
由于同步方法会自动同步线程的缓存与主内存中的变量,所以此时count变量无需设为volatile。

volatile的错误用法2

public class VolatileTest2 {    private volatile int lower = 0;    private volatile int upper = 10;    public void setLower(int value) {        if (value > upper)            throw new IllegalArgumentException();        lower = value;    }    public void setUpper(int value) {        if (value < lower)            throw new IllegalArgumentException();        upper = value;    }    public void test() {        new Thread(new Runnable() {            @Override            public void run() {                setLower(5);                System.out.println("lower:" + lower + ",upper:" + upper);            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                setUpper(3);                System.out.println("lower:" + lower + ",upper:" + upper);            }        }).start();    }    public static void main(String[] args) {        new VolatileTest2().test();    }}

上述代码运行时,我们期望的结果是出Exception。
如果第8行lower=value和第13行upper=value上打上断点,就会出现我们不希望出现的结果。lower:5,upper:3
正确的做法时将setLower和setUpper改成同步方法。

应该使用volatile的场合:

public class VolatileTest3 {    private long temp = 0x7fffffff00000000L;    private boolean running = true;    public void test() {        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                long a;                while (running) {                    a = temp;                    if (a != 0x7fffffff00000000L && a != 0x00000000ffffffffL) {                        System.out.println(Long.toHexString(a));                    }                }            }        });        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < Integer.MAX_VALUE; i++) {                    temp = 0x7fffffff00000000L;                }                running = false;            }        });        Thread t3 = new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < Integer.MAX_VALUE; i++) {                    temp = 0x00000000ffffffffL;                }                running = false;            }        });        t1.start();        t2.start();        t3.start();    }        public static void main(String[] args) {        new VolatileTest3().test();    }}

上述代码的运行结果,我们期望是没有输出,但是实际运行结果是有输出。输出0或7fffffffffffffff,每次运行结果都不一样。
正确的做法是将temp变量变为volatile。

原创粉丝点击