单例模式
饿汉式
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题。
优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
缺点:类加载的时候就初始化,不管用与不用都占着空间,一旦单例对象相对复杂,浪费了很多内存,有创建后不用。
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:00
*/
public class HungrySingleton {
private static final HungrySingleton HungrySingleton = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return HungrySingleton;
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:00
*/
public class HungryStaticSingleton {
private static final HungryStaticSingleton HUNGRY_STATIC_SINGLETON;
static {
HUNGRY_STATIC_SINGLETON = new HungryStaticSingleton();
}
private HungryStaticSingleton() {}
public static HungryStaticSingleton getInstance() {
return HUNGRY_STATIC_SINGLETON;
}
}
懒汉式
懒汉式单例的特点是:被外部类调用的时候内部类才会加载
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton {
private static LazySimpleSingleton lazySimpleSingleton;
private LazySimpleSingleton(){}
//这个写法在多线程时候执行,会出现线程安全问题。
public static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
/**
* @author DK
* @version 1.0
* @date 2020-06-08 10:10
**/
public class Main {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
try {
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(LazySimpleSingleton.getInstance());
}).start();
countDownLatch.countDown();
}
}
}
这样就会出现线程安全问题
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton {
private static LazySimpleSingleton lazySimpleSingleton;
private LazySimpleSingleton(){}
//为了线程安全我们往往会加 synchronized ,但同一时间大量调用这个获取单例方式就会拥堵获取对象相率相当低
public static synchronized LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton {
//volatile 是禁止指令重排序
private volatile static LazySimpleSingleton lazySimpleSingleton;
private LazySimpleSingleton(){}
//这里我们会采用double check 机制
public static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
synchronized (LazySimpleSingleton.class) {
if (lazySimpleSingleton == null)
lazySimpleSingleton = new LazySimpleSingleton();
}
}
return lazySimpleSingleton;
}
}
但还是不能保证会获取单例不会被破坏
/**
* @author DK
* @version 1.0
* @date 2020-06-08 10:10
**/
public class Main {
public static void main(String[] args) throws Exception{
LazySimpleSingleton lazySimpleSingleton = LazySimpleSingleton.getInstance();
//我们还可以通过反射去把单例对象创建出来
Class<LazySimpleSingleton> lazySimpleSingletonClass = LazySimpleSingleton.class;
Constructor<? extends LazySimpleSingleton> declaredConstructor = lazySimpleSingletonClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
LazySimpleSingleton lazySimpleSingleton1 = declaredConstructor.newInstance();
System.out.println(lazySimpleSingleton==lazySimpleSingleton1);//结果是flase
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton {
private volatile static LazySimpleSingleton lazySimpleSingleton;
private static boolean initFlag;
private LazySimpleSingleton(){
synchronized (this) {
if (initFlag) {
throw new RuntimeException("不允许创建多个实例");
}else {
initFlag = true;
lazySimpleSingleton = this;
}
}
}
public static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
synchronized (LazySimpleSingleton.class) {
if (lazySimpleSingleton == null)
lazySimpleSingleton = new LazySimpleSingleton();
}
}
return lazySimpleSingleton;
}
}
还可以通过内部类
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton {
//默认使用LazySimpleSingleton的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazySimpleSingleton(){
if (Holder.lazySimpleSingleton != null) {
throw new RuntimeException("不允许创建多个实例");
}
}
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static LazySimpleSingleton getInstance() {
//在返回结果以前,一定会先加载内部类
return Holder.lazySimpleSingleton;
}
//默认不加载
private static class Holder{
private static final LazySimpleSingleton lazySimpleSingleton = new LazySimpleSingleton();
}
}
但还可以通过序列化方式进行破快
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
//要实现序列化结果
public class LazySimpleSingleton implements Serializable{
//默认使用LazySimpleSingleton的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazySimpleSingleton(){
if (Holder.lazySimpleSingleton != null) {
throw new RuntimeException("不允许创建多个实例");
}
}
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static LazySimpleSingleton getInstance() {
//在返回结果以前,一定会先加载内部类
return Holder.lazySimpleSingleton;
}
//默认不加载
private static class Holder{
private static final LazySimpleSingleton lazySimpleSingleton = new LazySimpleSingleton();
}
}
/**
* @author DK
* @version 1.0
* @date 2020-06-08 10:10
**/
public class Main {
public static void main(String[] args) throws Exception{
LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(instance);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
LazySimpleSingleton o = (LazySimpleSingleton)objectInputStream.readObject();
System.out.println(o == instance); //结果还是false
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/6 22:06
*/
public class LazySimpleSingleton implements Serializable{
//默认使用LazySimpleSingleton的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazySimpleSingleton(){
if (Holder.lazySimpleSingleton != null) {
throw new RuntimeException("不允许创建多个实例");
}
}
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static LazySimpleSingleton getInstance() {
//在返回结果以前,一定会先加载内部类
return Holder.lazySimpleSingleton;
}
//默认不加载
private static class Holder{
private static final LazySimpleSingleton lazySimpleSingleton = new LazySimpleSingleton();
}
//加上这个方法再试下 这时结果是true的
public Object readResolve() {
return Holder.lazySimpleSingleton;
}
}
为什么加上readResolve 反序列化的对象是原来的对象 先来看下ObjectInputStream readObject 这个方法
点击32行进去看获取反序列化的方法
滑到1574 对象类型 点击进去 readOrdinaryObject
看到2078行这里 有个hasReadResolveMethod 点进去
然后跳回desc.hasReadResolveMethod()下面继续看
/**
* 重写 readResolve方法,只不过是覆盖反序列化出来的对象
* 实际上还是创建了两次,在jvm层面上,相对比较安全
* 之前反序列化的对象会被gc 回收
*/
public Object readResolve() {
return Holder.lazySimpleSingleton;
}
枚举式单例 如果有看过Effective Java这本书应该知道 也值得去看下
/**
* @author DK
* @version 1.
* 0
* @date 2020/6/7 6:44 下午
*/
public enum EnumSingleton {
Singleton;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/8 21:50
*/
public class Main {
public static void main(String[] args) {
EnumSingleton enumSingleton = EnumSingleton.Singleton;
EnumSingleton singleton = EnumSingleton.Singleton;
System.out.println(enumSingleton == singleton);//true
enumSingleton.setData(new Object());
System.out.println(enumSingleton == singleton);//true
}
}
即使面对反射攻击和复杂序列,也绝对保证多次实例化,为什么呢 ,可以通过jad反编译一下EnumSingleton
//这个就是通过jad 反编译EnumSingleton.class文件的结果 jad可以百度直接下载
public final class EnumSingleton extends Enum
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(singleton/hungry/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static final EnumSingleton Singleton;
private Object data;
//定义未初始化
private static final EnumSingleton $VALUES[];
//static 初始化 属于饿汉式写法,本身线程安全
static
{
Singleton = new EnumSingleton("Singleton", 0);
$VALUES = (new EnumSingleton[] {
Singleton
});
}
}
/**
* @author dk
* @version 1.0
* @date 2020/6/8 21:50
*/
public class Main {
public static void main(String[] args) throws Exception{
EnumSingleton enumSingleton = EnumSingleton.Singleton;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(enumSingleton);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
EnumSingleton enumSingleton1 = (EnumSingleton) objectInputStream.readObject();
System.out.println(enumSingleton == enumSingleton1);//true 为什么呢 要去挖一下源码了
}
}
这是点击枚举类型 readEnum方法进去
看下反射能不能破坏,之前我们通过jad看到他私有的构造器是这样的
/**
* @author dk
* @version 1.0
* @date 2020/6/8 21:50
*/
public class Main {
public static void main(String[] args) throws Exception{
EnumSingleton enumSingleton = EnumSingleton.Singleton;
Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumSingleton singleton = constructor.newInstance("Singleton", 0);
System.out.println(enumSingleton==singleton); //throw Cannot reflectively create enum objects
}
}
为什么会报这个错呢,咱们点进去看newInstance 看创建实例过程
注册式
/**
* 注册时登记
* @author DK
* @version 1.0
* @date 2020-06-08 14:50
**/
public class ContainerSingleton {
//ConcurrentHashMap是线程安全的 采用的是分段锁
private static final Map<String, Object> LOC = new ConcurrentHashMap<>();
public static Object getBean(String className) {
//这里为什么要加synchronized,虽然ConcurrentHashMap是线程安全,但是只是针对他内部, 但getBean这个不是线程安全
synchronized (LOC) {
if (LOC.containsKey(className))
return LOC.get(className);
else {
Object object = null;
try {
Class<?> clazz = Class.forName(className);
object = clazz.newInstance();
LOC.put(className, object);
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
}
}
}