Volatile - 用途

        首先,这里不是赘述volatile关键字的原理,主要讨论的是“为什么volatile线程不安全,却仍要使用?”,这个问题是之前我在学习volatile关键字时困扰的。

同步机制

  1. synchronized
  2. volatile

第一种synchronized同步代码块,肯定能达到线程安全,但是性能较低;相比较而言,第二种,volatile就比较轻量级、简单、开销低、不会线程阻塞,但同时,也没办法完全保障线程安全,不具备原子性

volatile - 使用条件

  1. 多个变量之间,或某个变量的当前值和修改后的值之间没有约束。
  2. 在某些情况下,如果读操作远远大于写操作,volatile变量可以提供优于锁的性能。
  3. 应用条件:
  • 对变量的写操作,不依赖于当前值;--->不能用作线程安全计数器
  • 该变量不包含在具有其他变量的不变式中。     

volatile - 适用场景

  1. 状态标志,比如Boolean。
    用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。
    volatile boolean shutdownRequested;
    
    ...
    
    public void shutdown() { shutdownRequested = true; }
    
    public void doWork() { 
        while (!shutdownRequested) { 
            // do stuff
        }
    }
  2. 一次性安全发布
    将对象引用定义为 volatile 类型。后台线程在启动阶段从数据库加载一些数据。其他代码在能够利用这些数据时,在使用之前将检查这些数据是否曾经发布过。
    public class BackgroundFloobleLoader {
        public volatile Flooble theFlooble;
    
        public void initInBackground() {
            // do lots of stuff
            theFlooble = new Flooble();  // this is the only write to theFlooble
        }
    }
    
    public class SomeOtherClass {
        public void doWork() {
            while (true) { 
                // do some stuff...
                // use the Flooble, but only if it is ready
                if (floobleLoader.theFlooble != null) 
                    doSomething(floobleLoader.theFlooble);
            }
        }
    }
  3. 独立观察
    定期 “发布” 观察结果供程序内部使用。
    例如,假设有一种环境传感器能够感觉环境温度。一个后台线程可能会每隔几秒读取一次该传感器,并更新包含当前文档的 volatile 变量。然后,其他线程可以读取这个变量,从而随时能够看到最新的温度值。
    public class UserManager {
        public volatile String lastUser;
    
        public boolean authenticate(String user, String password) {
            boolean valid = passwordIsValid(user, password);
            if (valid) {
                User u = new User();
                activeUsers.add(u);
                lastUser = user;
            }
            return valid;
        }
    }
  4. “volatile bean” 模式
    JavaBean 被用作一组具有 getter 和/或 setter 方法 的独立属性的容器。volatile bean 模式的基本原理是:很多框架为易变数据的持有者(例如 HttpSession)提供了容器,但是放入这些容器中的对象必须是线程安全的。
    在 volatile bean 模式中,JavaBean 的所有数据成员都是 volatile 类型的,并且 getter 和 setter 方法必须非常普通。
    @ThreadSafe
    public class Person {
        private volatile String firstName;
        private volatile String lastName;
        private volatile int age;
    
        public String getFirstName() { return firstName; }
        public String getLastName() { return lastName; }
        public int getAge() { return age; }
    
        public void setFirstName(String firstName) { 
            this.firstName = firstName;
        }
    
        public void setLastName(String lastName) { 
            this.lastName = lastName;
        }
    
        public void setAge(int age) { 
            this.age = age;
        }
    }
  5. 开销较低的读-写锁策略
    如果读操作远远超过写操作,您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。
    @ThreadSafe
    public class CheesyCounter {
        // Employs the cheap read-write lock trick
        // All mutative operations MUST be done with the 'this' lock held
        @GuardedBy("this") private volatile int value;
    
        public int getValue() { return value; }
    
        public synchronized int increment() {
            return value++;
        }
    }

猜你喜欢

转载自my.oschina.net/xiaowangqiongyou/blog/1788323