volatile是java中的一种轻量级同步机制,主要应用于原子级操作, 相比于synchronized关键字, 不会引起线程上下文的切换和调度。
- java中的原子性操作
- 基本类型的读取和赋值
- 所有引用的赋值
- jaca.concurrent.Atomic.*下的所有操作
- volatile变量的特性
- 当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中
- 导致其他线程中的volatile变量无效
- 禁止指令重排(在并发环境下,指令重排会导致结果不一致)
volatile不适用非原子性操作示例:
class Test{
public int a = 0;
public void increase(){
a++;
}
public static void main(String[] args) throws InterruptedException {
System.out.println();
final Test test = new Test();
for(int i = 0; i < 10; i++){
new Thread(){
@Override
public void run() {
for(int j = 0 ; j < 1000; j++){
test.increase();
}
};
}.start();
}
while (Thread.activeCount() > 2){
Thread.yield(); // 当前线程放弃对处理器的使用
}
System.out.println(test.a);
}
}
代码输出结果小于10*1000, 因为a++不是一个原子性操作,由读取、(运算)a+1, (赋值)a = a + 1三个步骤组成。
- 解决方法
- synchronized关键字
- lock
- CAS
单例模式
适用于全局频繁使用创建与销毁。例如全局计数器、生产唯一序列号、I/O与数据库的连接。
-
饿汉式
class SingleTon{ private static SingleTon singleTon = new SingleTon(); private SingleTon(){} public static SingleTon getSingleTon() { return singleTon; } public static void main(String[] args) { SingleTon singleTon1 = SingleTon.getInstance(); } }
将构造函数设为private,这样这个实例就唯一了, 缺点是有可能由于其他的静态方法导致类加载,无法达到lazy loading的效果。
-
懒汉式
class SingleTon{ private static SingleTon instance = null; private SingleTon() { } public static SingleTon getInstance() { if(instance == null){ instance = new SingleTon(); } return instance; } public static void main(String[] args) { SingleTon singleTon2 = SingleTon.getInstance(); } }
这种懒汉式的好处是按需加载,只会通过getInstance方法创建这个唯一示例,但是很明显的是线程不安全的。
解决方法:加同步锁
-
单锁的懒汉式
class SingleTon{ private static SingleTon instance = null; private SingleTon() { } public static synchronized SingleTon getInstance() { if(instance == null){ instance = new SingleTon(); } return instance; } public static void main(String[] args) { SingleTon singleTon3 = SingleTon.getInstance(); } }
这种加锁的方式十分耗费资源
-
双重锁
class SingleTon{ private volatile static SingleTon instance = null; private SingleTon() { } public static synchronized SingleTon getInstance() { if(instance == null){ synchronized (SingleTon.class){ if(instance == null){ instance = new SingleTon(); // 不是原子性操作 } } } return instance; } public static void main(String[] args) { SingleTon singleTon4 = SingleTon.getInstance(); } }
这种方式是线程安全的,且在并发情况下效率较高,但是实现复杂
-
静态内部类
class SingleTon{ private static class SingleTonHolder{ private static final SingleTon singleTon = new SingleTon(); } private SingleTon() { } public static final synchronized SingleTon getInstance() { return SingleTonHolder.singleTon; } public static void main(String[] args) { SingleTon singleTon5 = SingleTon.getInstance(); } }
首先,只能能过getInstance实例化,且不会被改变,是线程安全的。