# 单例模式
# 饿汉式(线程安全)
public class Singleton01 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);//true
System.out.println(singleton1.hashCode() == singleton2.hashCode());//true
}
}
class Singleton {
// 构造私有化对象,防止外部new
private Singleton() {
}
// 本类内部创建对象实例
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
优点
写法简单,类加载的时候就会实例化,避免了线程同步问题
缺点
类加载的时候就实例化,没有达到lazy loading效果
# 懒汉式(线程不安全)
public class Singleton02Test {
public static void main(String[] args) {
Singleton021 singleton1 = Singleton021.getInstance();
Singleton021 singleton2 = Singleton021.getInstance();
System.out.println(singleton1 == singleton2);
System.out.println(singleton1.hashCode() == singleton2.hashCode());
}
}
// 多线程不安全
class Singleton021 {
// 构造私有化对象,防止外部new
private Singleton021() {
}
// 本类内部创建对象实例
private static Singleton021 instance;
// 使用的时候才去创建
public static Singleton021 getInstance() {
if (instance == null){
instance = new Singleton021();
}
return instance;
}
}
# 双重校验锁
public class Singleton03Test {
public static void main(String[] args) {
Singleton03 singleton3 = Singleton03.getInstance();
Singleton03 singleton4 = Singleton03.getInstance();
System.out.println(singleton3 == singleton4);
System.out.println(singleton3.hashCode() == singleton4.hashCode());
}
}
// 线程安全,效率太低
class Singleton03 {
// 构造私有化对象,防止外部new
private Singleton03() {
}
// 本类内部创建对象实例
private static volatile Singleton03 instance;
public static Singleton03 getInstance() {
if (instance == null) {
synchronized (Singleton03.class){
if (instance == null){
instance = new Singleton03();
}
}
}
return instance;
}
}
instance = new Singleton03() 这句话分为三步执行:
- 为 singleton 分配内存空间
- 初始化 singleton
- singleton 指向分配的内存空间。
Java 语言规规定了线程执行程序时需要遵守 intra-thread semantics。**intra-thread semantics ** 保证重排序不会改变单线程内的程序执行结果。这个重排序在没有改变单线程程序的执行结果的前提下,可以提高程序的执行性能。
虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就带来一些问题。
由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 getInstance() 后发现 singleton 不为空,因此返回 singleton, 但是此时的 singleton 还没有被初始化。
使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。
# 静态内部类
public class Singleton04Test {
public static void main(String[] args) {
Singleton01 singleton1 = Singleton01.getInstance();
Singleton01 singleton2 = Singleton01.getInstance();
System.out.println(singleton1 == singleton2);
System.out.println(singleton1.hashCode() == singleton2.hashCode());
}
}
class Singleton04 {
// 构造私有化对象,防止外部new
private Singleton04() {
}
/**
* 静态内部类在外部类装载的时候,不会加载
* 调用getInstance的时候会去加载
* JVM加载类的时候是线程安全的
* */
private static class Singleton04Instance {
private static final Singleton04 singleton04 = new Singleton04();
}
public static Singleton04 getInstance() {
return Singleton04Instance.singleton04;
}
}
类的静态属性只有在第一次加载的时候才会初始化
# 枚举
public class Singleton05Test {
public static void main(String[] args) {
Singleton05 singleton1 = Singleton05.INSTANCE;
Singleton05 singleton2 = Singleton05.INSTANCE;
System.out.println(singleton1 == singleton2);
System.out.println(singleton1.hashCode() == singleton2.hashCode());
System.out.println(singleton1.say());
}
}
enum Singleton05 {
INSTANCE;
public String say(){
return "Are You OK?";
}
}