简单学:并发编程之volatile

关于java并发编程的相关文章都是阅读了《java并发编程实战》以后的读书笔记总结,另外本文还参考和引用了Java 理论与实践: 正确使用 Volatile 变量html

在java的锁机制中(例如synchronized),主要包含了两种特性,即原子性(互斥)和可见性。原子性即一次只容许一个线程可以持有某个特定的锁,并访问其代码块。所以原子性能够用于实现对共享数据对协调访问,一次只有一个线程能够访问其共享对象。java

volatile保证可见性

java中的volatile关键字能够被认为是一种轻量级的synchronized机制,访问volatile变量的时候并不会执行加锁操做,不会致使线程阻塞,同时它又和锁机制同样,都具有可见性的特性(可是并不具有原子特性)。当把变量生命为volatile类型后,编译器与运行时都会注意到这个变量是共享的,所以不会把这个变量上的操做与其余内存操做一块儿重排序。volatile变量不会被缓存在寄存器或者其余处理器不可见的地方。所以在读取volatile变量的时候老是会返回最新写入的值。编程

volatile提供线程安全

volatile变量能够用于提供线程安全,可是必须同时知足如下两个条件:缓存

  • 对变量的写操做不依赖于当前的值安全

  • 该变量没有包含在具备其余变量的不变式之中并发

正确使用volatile关键字

在使用volatile关键字的时候,要始终牢记一个原则--只有在状态真正独立于程序内其余内容时才能使用 volatilethis

  • 状态标识:spa

    volatile变量能够做为一个布尔状态标识,用于指示发生一次重要性时间,例如中止线程等线程

class Test {
    static volatile boolean isRunning;

    private static class TestThread extends Thread {
        public void run() {
            while (!isRunning) {
                System.out.println("the thread is still running");
            }
        }
    }

    public static void main(String[] args) {
        new TestThread().start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isRunning = true;
    }
}复制代码

在上面的例子中,咱们在isRunning这个布尔变量前面加上了volatile进行修饰,以确保在main线程当中对它的修改可以被咱们开启的子线程所看到。(若是咱们没有添加volatile关键字,有可能子线程会持续运行而看不到isRunning的值的改变)code

  • 一次性安全发布:

    在缺少同步的状况下,可能会遇到某个对象引用的更新值(由另外一个线程写入)和该对象状态的旧值同时存在。经过使用volatile关键字,咱们能够实现安全发布对象。

public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) {
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null)
                doSomething(floobleLoader.theFlooble);
        }
    }
}复制代码

⚠️上面的安全发布的必要条件是:被发布的对象必须是线程安全的,或者是有效的不可变对象(有效不可变意味着对象的状态在发布以后永远不会被修改)。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;
    }
}复制代码

⚠️上面这种用例要求被发布的值是有效不可变的 —— 即值的状态在发布后不会更改

  • 开销较低的读-写锁策略:

    若是对于某个变量的值的读操做远远超过写操做,咱们能够经过将内置锁(帮助咱们实现操做的原子性)和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++;
    }
}复制代码
相关文章
相关标签/搜索