2. 与线程安全的一些关键字volatile,final,static
(1)volatile:这个关键字可以起到确保一个变量对其他线程而言是可见的,解决可见性问题。如将Counter类进行如下修改,不存在原子性问题,但存在可见性问题:
例如,如果其他线程调用了setCounter方法对counter的值进行了修改为5,之后其他线程调用getCounter方法获取counter的值,并不一定会获取到5。但是若将counter的声明加上volatile的关键字,则可解决可见性问题,就是可确保一个线程可以看到另一个线程对状态的改变。
(3)static关键字,static修饰的变量或初始化代码块中的变量,可以保证到初始化结束的这一个步骤之前的操作状态,对其他线程是可见的,但是,若对象的状态是会变化的,那么也需要进行同步。静态初始化是JVM完成的,发生在类的初始阶段,即类被加载后类被任意线程使用之前。JVM要在初始化期间获得一个锁,这个锁每个线程都至少会用到一次,来确保一个类是否已被加载;这个锁也保证了静态初始化期间,内存写入的结果自动地对所有的线程是可见的。
(1)volatile:这个关键字可以起到确保一个变量对其他线程而言是可见的,解决可见性问题。如将Counter类进行如下修改,不存在原子性问题,但存在可见性问题:
public class Counter {
private int counter;
public synchronized int getCounter() {
return counter;
}
public synchronized void setCounter(int counter) {
this.counter = counter;
}
}
例如,如果其他线程调用了setCounter方法对counter的值进行了修改为5,之后其他线程调用getCounter方法获取counter的值,并不一定会获取到5。但是若将counter的声明加上volatile的关键字,则可解决可见性问题,就是可确保一个线程可以看到另一个线程对状态的改变。
public class Counter {
private volatile int counter;
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
(2) final关键字:这个关键字主要用于辅助创建不可变状态,如果对象已经被正确的构建了(其实对象如何被正确构建,这是一个复杂的主题,简单来说,不要在构造函数中暴露this给其他线程使用),那么final修饰的关键字值是对所有的其他线程都是可见的,同时一个对象的状态是不会变化的,因此在多个线程中使用此对象也是线程安全的。
public class Counter {
private final int counter;
public Counter() {
this.counter = 5;
}
public int getCounter() {
return counter;
}
}
(3)static关键字,static修饰的变量或初始化代码块中的变量,可以保证到初始化结束的这一个步骤之前的操作状态,对其他线程是可见的,但是,若对象的状态是会变化的,那么也需要进行同步。静态初始化是JVM完成的,发生在类的初始阶段,即类被加载后类被任意线程使用之前。JVM要在初始化期间获得一个锁,这个锁每个线程都至少会用到一次,来确保一个类是否已被加载;这个锁也保证了静态初始化期间,内存写入的结果自动地对所有的线程是可见的。