13.volatile与synchronized比较

synchronized,volatile都解决了共享变量 value 的内存可见性问题,但是前者是独占锁,同时只能有一个线程调用 get()方法,其他调用线程会被阻塞, 同时会存在线程上下文切换和线程重新调度的开销,这也是使用锁方式不好的地方。 而后者是非阻塞算法,不会造成线程上下文切换的开销。

volatile 虽然提供了可见性保证,但并不保证操作的原子性。

一般在什么时候才使用 volatile 关键字呢?

  • 写入变量值不依赖、变量的当前值时。 因为如果依赖当前值,将是获取一计算一写入 三步操作,这三步操作不是原子性的,而 volatile 不保证原子性。
  • 读写变量值时没有加锁。 因为加锁本身已经保证了内存可见性,这时候不需要把变量声明为 volatile 的。
/**
 * volatile不能代替锁
 * volatile无法保证i++的原子性操作
 */
public class VolatileDemo {
    static volatile int i = 0;
    public static class PlusTask implements Runnable {
        @Override
        public void run() {
//            synchronized (VolatileDemo.class){
                for (int j = 0; j < 10000; j++) {
                    i++;
                }
//            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Thread[] threads = new Thread[10];
        for (int a = 0; a < 10; a++) {
            threads[a] = new Thread(new PlusTask());
            threads[a].start();
        }
        for (int a = 0; a < 10; a++) {
            threads[a].join();
        }
        System.out.println(i);//i的值小于10000
    }
}
//可见性
public class NoVisibility {
    private static volatile boolean ready;
    private static int number;
    public static class ReadThread extends Thread{
        @Override
        public void run() {
            while (!ready);  //没使用volatile声明ready前,主线程修改ready的状态,ReadThread无法"看到"主线程中的修改
            System.out.println(number);
        }
    }
    public static void main(String[] args) throws InterruptedException{
        ReadThread readThread = new ReadThread();
        readThread.start();
        Thread.sleep(1000);
        number = 42;
        ready = true;
        Thread.sleep(1000);
    }
}

posted @ 2019-08-19 09:14  fly_bk  阅读(204)  评论(0编辑  收藏  举报