单例模式
定义 :
保证全局绝对只有一个实例对象
单例类必须自己创建自己的唯一实例
单例必须给所有其他对象提供这一实例
单例的实现方式
饿汉模式
类加载时就实例化对象
/*
饿汉单例模式
特征 类加载时就实例化对象
优点 线程安全 还没线程时就实例化了 所以线程绝对安全的
缺点 可能会带来内存浪费占茅坑不拉屎的想象 适用于实例数量少的场景
*/
public class HungrySingleton {
// 类加载顺序 先静态后动态 先属性后方法 先上后下
// 类加载时就会被实例化
private static HungrySingleton instance = new HungrySingleton();
// 构造函数必须私有 这样才不会被外部对象实例化
private HungrySingleton(){}
// 获取唯一可用的对象 (获取实例有则返回无则创建) static让全局可访问
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉模式
被外部类调用时才初始化实例
/**
* 懒汉模式
* 特征 被外部类调用时才初始化实例
* 优点 不浪费内存 不提前初始化 用时再初始化
* 缺点 多线程下不安全
*
*/
public class LazySingleton {
private static LazySingleton instance = null;
LazySingleton(){ }
public static LazySingleton getInstance() {
if(null==instance){
instance = new LazySingleton();
}
return instance;
}
}
懒汉模式升级 双重检查加锁 double-checked locking
package com.liao.edu.lazy;
/**
* 懒汉模式+双重检查加锁
* 特征 被外部类调用时才初始化实例
* 优点 调用方法有条件触发锁 锁的粒度更细 线程安全的
* 缺点 还是会触发锁,会被反序列化破坏
*/
public class LazyDoubleCheckSingleton {
//volatile 防止指令重拍
//因为 instance = new DoubleCheckLazySingleton(); 实例化过程并不是原子性的
//所以加volatile防止指令重排(123顺序可能错乱 导致其他线程调用时instance!=null )
private volatile static LazyDoubleCheckSingleton instance = null;
//私有化保证不被外部初始化
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance() {
//有条件加锁避免调用就上锁 即减少同步锁的竞争
if(null==instance){
synchronized (LazyDoubleCheckSingleton.class){
//保证单例
if(null==instance){
instance = new LazyDoubleCheckSingleton();//不是原子操作
//1.创建内存空间
//2.在内存空间内创建对象
//3.将内存空间复制给变量instance
}
}
}
return instance;
}
}
懒汉模式升级之 静态内部类
package com.liao.edu.lazy;
/**
* 懒汉模式特征 被外部类调用时才初始化实例
* 懒汉模式+静态内部类 利用类的加载机制-兼顾了饿汉式单例的空间浪费和懒汉式单例synchronized的性能问题 完美屏蔽了2个缺点
* 优点 利用java本身语法特点,性能高 内部类一定是在方法调用之前初始化,巧妙地避免了线程安全问题。 不能被反射破坏
* 缺点 不优雅
*/
public class LazyStaticInnerClassSingleton {
//默认使用的时候 会先初始化内部类
//如果没有使用的话 内部类是不加载的
private LazyStaticInnerClassSingleton(){
if(InnerClass.lazy!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
// static 为了使单例的空间共享
// final 保证方法不回被重写,重载
public static final LazyStaticInnerClassSingleton getInstance() {
//在返回结果之前,一定会先加载内部类
return InnerClass.lazy;
}
//默认不加载
private static final class InnerClass {
private static LazyStaticInnerClassSingleton lazy = new LazyStaticInnerClassSingleton();
}
}
测试静态内部类单例
package com.liao.edu;
import com.liao.edu.lazy.LazyStaticInnerClassSingleton;
import java.lang.reflect.Constructor;
public class StaticInnerLazySingletonTest {
public static void main(String[] args) {
try {
Class<?> clazz = LazyStaticInnerClassSingleton.class;
//通过反射拿到私有构造方法
Constructor c = clazz.getDeclaredConstructor(null);
//强制访问
c.setAccessible(true);
//暴力初始化
Object obj = c.newInstance();
Object obj2 = c.newInstance();
System.out.println(obj==obj2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果
false
Process finished with exit code 0
登记式/注册式单例之枚举单例
package com.liao.edu.register;
/**
* 枚举单例
* 枚举天生不能被反射破坏 Cannot reflectively create enum objects
* 枚举天生线程安全 private volatile transient Map<String, T> enumConstantDirectory = null;
* 优点 写法优雅简单 防止反射 线程安全
* 缺点 底层是饿汉式的实现 内存浪费 不能大量使用
*/
public enum EnumSingleton {
INSTANCE;
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
测试
public class EnumSingletonTest {
public static void main(String[] args) {
EnumSingleton n1 = EnumSingleton.getInstance();
n1.setObj("abc");
EnumSingleton n2 = EnumSingleton.getInstance();
n2.setObj("abc");
System.out.println(n1.getObj());
System.out.println(n2.getObj());
System.out.println(n1.getObj()==n2.getObj());
Class clazz = EnumSingleton.class;
try {
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
c.setAccessible(true);
c.newInstance("liao",666);
}catch (Exception e){
e.printStackTrace();
}
}
}
返回结果
abc
abc
true
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
at com.liao.edu.EnumSingletonTest.main(EnumSingletonTest.java:22)
Process finished with exit code 0
注册式单例之容器单例
package com.liao.edu.register;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 容器注册单例
* 适用于单例非常多的情况,便于管理
*/
public class ContainerSingleton {
private static Map<String,Object> map = new ConcurrentHashMap<String,Object>();
//无参私有的构造函数
private ContainerSingleton(){}
public static Object getInstance(String className) {
if(!map.containsKey(className)){
synchronized (ContainerSingleton.class){
if(!map.containsKey(className)){
try{
map.put(className,Class.forName(className).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return map.get(className);
}
}
测试
package com.liao.edu;
import com.liao.edu.register.ContainerSingleton;
public class ContainerSingletonTest {
public static void main(String[] args) {
EnumSingletonTest instance = (EnumSingletonTest) ContainerSingleton.getInstance("com.liao.edu.EnumSingletonTest");
EnumSingletonTest instance2 = (EnumSingletonTest) ContainerSingleton.getInstance("com.liao.edu.EnumSingletonTest");
System.out.println(instance);
System.out.println(instance2);
System.out.println(instance==instance2);
}
}
返回结果
com.liao.edu.EnumSingletonTest@135fbaa4
com.liao.edu.EnumSingletonTest@135fbaa4
true
Process finished with exit code 0