懒汉式 和 饿汉时
public class Singleton {
/**
* 懒汉式:在真正 需要使用对象时 才去创建该单例类对象
* 在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象,否则则先执行实例化操作。
*/
//1.0
//两个线程同时判断singleton为空,那么它们都会去实例化一个Singleton对象,这就变成双例了
// private static Singleton singleton;
// private Singleton(){}
// public static Singleton getInstance() {
// if (singleton == null) {
// singleton = new Singleton();
// }
// return singleton;
// }
//2.0 解决线程安全问题
//每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。
// private static Singleton singleton;
// private Singleton(){}
// public static synchronized Singleton getInstance() {
// if (singleton == null) {
// singleton = new Singleton();
// }
// return singleton;
// }
// private static Singleton singleton;
// private Singleton(){}
// public static Singleton getInstance() {
// synchronized (Singleton.class) {
// if (singleton == null) {
// singleton = new Singleton();
// }
// }
// return singleton;
// }
//3.0 解决卡顿现象 Double Check(双重校验) + Lock(加锁)
//优化性能:如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例,所以直接在方法上加锁的方式就被废掉了,因为这种方式无论如何都需要先获取
//存在最后一个问题:指令重排
// private static Singleton singleton;
// private Singleton(){}
// public static Singleton getInstance() {
// if (singleton == null) { //如果singleton不为空,则直接返回对象,不需要获取锁;而如果多个线程发现singleton为空,则进入分支;
// synchronized (Singleton.class) { //多个线程尝试争抢同一个锁,只有一个线程争抢成功,第一个获取到锁的线程会再次判断singleton是否为空,因为singleton有可能已经被之前的线程实例化
// if (singleton == null) { //发现singleton已经不为空了,则不会再new一个对象,直接返回对象即可
// singleton = new Singleton();
// }
// }
// }
// return singleton;
// }
//4.0 解决指令重排:使用volatile关键字修饰的变量
//指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
// 有序性:可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换
// 内存可见性:每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。
// private static volatile Singleton singleton;
// private Singleton(){}
// public static Singleton getInstance() {
// if (singleton == null) { //如果singleton不为空,则直接返回对象,不需要获取锁;而如果多个线程发现singleton为空,则进入分支;
// synchronized (Singleton.class) { //多个线程尝试争抢同一个锁,只有一个线程争抢成功,第一个获取到锁的线程会再次判断singleton是否为空,因为singleton有可能已经被之前的线程实例化
// if (singleton == null) { //发现singleton已经不为空了,则不会再new一个对象,直接返回对象即可
// singleton = new Singleton();
// }
// }
// }
// return singleton;
// }
/**
* 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
* 在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
*/
// private static final Singleton singleton = new Singleton();
// private Singleton() {}
// public static Singleton getInstance() {
// return singleton;
// }
}
枚举
/**
* 枚举实现单例的过程:在程序启动时,会调用Singleton的空参构造器,实例化好一个Singleton对象赋给INSTANCE,之后再也不会实例化
* 优势1:代码对比饿汉式与懒汉式来说,更加地简洁
* 优势2:它不需要做任何额外的操作去保证对象单一性与线程安全性
* 优势3:使用枚举可以防止调用者使用反射、序列化与反序列化机制强制生成多个单例对象,破坏单例模式。
*/
public enum Singleton2 {
INSTANCE;
Singleton2() {} //枚举创建对象了
}
//测试
//public enum Singleton2 {
// INSTANCE;
// Singleton2() {}
// //测试方法
// public static void test() {
// Singleton2 t1 = Singleton2.INSTANCE;
// Singleton2 t2 = Singleton2.INSTANCE;
// System.out.println(t1 == t2); //true
// }
// public static void main(String[] args) {
// test();
// }
//}