From:Java并发编程的艺术
- 目录
BiBi - 并发编程 -0- 开篇
BiBi - 并发编程 -1- 挑战
BiBi - 并发编程 -2- volatile
BiBi - 并发编程 -3- 锁
BiBi - 并发编程 -4- 原子操作
BiBi - 并发编程 -5- Java内存模型
BiBi - 并发编程 -6- final关键字
BiBi - 并发编程 -7- DCL
BiBi - 并发编程 -8- 线程
BiBi - 并发编程 -9- ReentrantLock
BiBi - 并发编程 -10- 队列同步器
BiBi - 并发编程 -11- 并发容器
BiBi - 并发编程 -12- Fork/Join框架
BiBi - 并发编程 -13- 并发工具类
BiBi - 并发编程 -14- 线程池
BiBi - 并发编程 -15- Executor框架
DCL实现单例
public class Singleton {
private volatile Singleton singleton;
// 构造函数是private,防止外部实例化
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) { // 第一次检查
synchronized (Singleton.class) {
if (singleton == null) { // 第二次检查,"double check"的由来
singleton = new Singleton();
}
}
}
return singleton;
}
}
Java中创建一个对象分为三个步骤:
1)在内存中开辟一块地址
2)对象初始化
3)将指针指向这块内存地址
Java中存在指令重排序现象。如果在新建Singleton对象的时候第2步和第3步发生了重排序,线程1将singleton指针指向了内存中的地址,但是此时我们的对象还没有初始化。这个时候线程2进来,看到singleton不是null,于是直接返回。这个时候错误就发生了,线程2拿到了一个没有经过初始化的对象。解决这个问题的思路也很简单,就是使用volatile防止指令重排序。
内部类实现单例
public class Singleton {
private Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
private static class SingletonHolder {
private static Singleton singleton = new Singleton();
}
}
基于ClassLoader的实现多线程同步,即利用classloder的机制来保证初始化instance时只有一个线程。JVM在类初始化阶段会获取一个锁,这个锁可以同步多个线程对同一个类的初始化。
枚举实现单例
public enum Singleton {
instance;
private Singleton(){}
public void doSomething(){}
}
优点:线程安全、保证只有一个实例、能够实现反序列化。
其它单例实现方式在反序列化时会通过【反射】去重新创建对象。若要避免需要手动实现private Object readResolve(){return singleton;}
网友评论