package cn.fllday.single;
/**
* 饿汉式 单例模式
* @author gssznb
*/
public class HungrySinglePatterns {
// 类进行初始化的时候,就立即加载这个对象。没有延时加载的优势。加载类时,线程是安全的。
private static HungrySinglePatterns instance = new HungrySinglePatterns();
private HungrySinglePatterns() { }
// 方法没有同步,调用率高
public static HungrySinglePatterns getInstance() {
return instance;
}
}
package cn.fllday.single;
/**
* 懒汉式单例模式
* @author gssznb
*
*/
public class LazyLoadSinglePatterns {
private static LazyLoadSinglePatterns instance;
/**
* 私有化构造器
*/
private LazyLoadSinglePatterns() { }
/**
* 因为是延时加载所以有可能出现线程同步的问题。所以要加上 同步块。
* 如果A线程执行 方法。 instance为null的时候, B线程又要执行方法,那么这个时候,两个线程都判断instance为null
* 就会创建出两个对象。 所以要加上synchronized。 导致调用效率不高。
* @return
*/
public static synchronized LazyLoadSinglePatterns getInstance() {
if (instance == null) {
return new LazyLoadSinglePatterns();
}
return instance;
}
}
package cn.fllday.single;
/**
* 双重检测锁实现单例模式
* 懒加载,调用率高。没有同步问题。但是因为jvm底层问题,容易出现问题,不推荐使用
* @author gssznb
*
*/
public class DoubuCheckLockedSinglePatterns {
private static DoubuCheckLockedSinglePatterns instance;
private DoubuCheckLockedSinglePatterns() {
// TODO Auto-generated constructor stub
}
public static DoubuCheckLockedSinglePatterns getInstance() {
if (instance == null) {
DoubuCheckLockedSinglePatterns dcl;
synchronized (DoubuCheckLockedSinglePatterns.class) {
dcl = instance;
if (dcl == null) {
synchronized (DoubuCheckLockedSinglePatterns.class) {
if (dcl == null) {
dcl = new DoubuCheckLockedSinglePatterns();
}
}
instance = dcl;
}
}
}
return instance;
}
}
package cn.fllday.single;
/**
* 静态内部类,实现懒加载单例模式
* 线程安全,懒加载,并且实现了延时加载。调用效率高
* @author gssznb
*
*/
public class StaticInteriorSinglePatterns {
private static class SingletonClassInstance{
private static final StaticInteriorSinglePatterns instance = new StaticInteriorSinglePatterns();
}
private StaticInteriorSinglePatterns() { }
public static StaticInteriorSinglePatterns getInstance() {
return StaticInteriorSinglePatterns.SingletonClassInstance.instance;
}
}
package cn.fllday.single;
/**
* 枚举测试实现单例模式
* @author gssznb
*
*/
public enum EnumSinglePatterns {
// 枚举元素本身就是单例对象
/**
* 避免了反射和反序列化的漏洞,调用效率比较高。但是没有懒加载的效果
*/
INSTANCE;
// 添加自己需要的操作。
public void singletonOperation() {
}
}
package cn.fllday.single;
public class Client {
public static void main(String[] args) {
HungrySinglePatterns instance1 = HungrySinglePatterns.getInstance();
HungrySinglePatterns instance2 = HungrySinglePatterns.getInstance();
HungrySinglePatterns instance3 = HungrySinglePatterns.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
EnumSinglePatterns instance4 = EnumSinglePatterns.INSTANCE;
EnumSinglePatterns instance5 = EnumSinglePatterns.INSTANCE;
System.out.println(instance4);
System.out.println(instance5);
}
}
cn.fllday.single.HungrySinglePatterns@15db9742
cn.fllday.single.HungrySinglePatterns@15db9742
cn.fllday.single.HungrySinglePatterns@15db9742
INSTANCE
INSTANCE
package cn.fllday.single;
import java.lang.reflect.Constructor;
/**
* 如果通过反射来构建单例的多个对象
* @author gssznb
*
*/
public class Client2 {
public static void main(String[] args) throws Exception {
HungrySinglePatterns2 instance1 = HungrySinglePatterns2.getInstance();
HungrySinglePatterns2 instance2 = HungrySinglePatterns2.getInstance();
System.out.println(instance1);
System.out.println(instance2);
Class clazz = (Class) Class.forName("cn.fllday.single.HungrySinglePatterns2");
// 获取该类的构造方法
Constructor c = clazz.getDeclaredConstructor(null);
// 设置可以跳过权限访问检测
c.setAccessible(true);
// 通过newInstance()方法 创建该类的对象
HungrySinglePatterns2 instance3 = c.newInstance();
HungrySinglePatterns2 instance4 = c.newInstance();
System.out.println(instance3);
System.out.println(instance4);
}
}
反射来创建多个对象是通过 反射来获取到 私有化的构造器来创建的对象。我们只需要在构造器中做一个判断就可以了。
解决方案:
/**
* 解决通过反射机制来创建多个单例对象
*/
private HungrySinglePatterns2() {
// 如果是第一次创建对象 就可以成功创建
// 如果第二次创建那么 instance对象就不为空,我们抛出一个异常
if (instance != null) {
throw new RuntimeException();
}
}
package cn.fllday.single;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
/**
* 如何通过序列化 来创建多个单例对象
* @author gssznb
*
*/
public class Client3 {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
HungrySinglePatterns2 instance1 = HungrySinglePatterns2.getInstance();
HungrySinglePatterns2 instance2 = HungrySinglePatterns2.getInstance();
System.out.println(instance1);
System.out.println(instance2);
// 通过序列化来创建多个对象
FileOutputStream fos = new FileOutputStream("Z:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance1);
// 通过反序列化来还原对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("z:/a.txt"));
HungrySinglePatterns2 instance3 = (HungrySinglePatterns2)ois.readObject();
System.out.println(instance3);
}
}
在单例类中, 写一个readReslove() 方法,则直接返回此方法指定的对象,而不需要单独再次创建对象
// 反序列化时,如果定义了readReslove()则直接返回此方法指定的对象,而不需要单独再次创建对象。
private Object readResolve() throws ObjectStreamException {
return instance;
}
饿汉式: 线程安全,调用率高。但是不能延时加载
懒汉式: 线程安全,调用率不高。可以延时加载。
双重检查锁: 由于JVM底层内部模型原因,偶尔会出现问题。不建议使用
静态内部类:线程安全,调用率高。延时加载
枚举式:线程安全,调用率高。不能延时加载。但是有天然的防止反射和序列化进行创建多个对象。
单例对象: 占用资源少 ,不需要延时加载。
单例对象: 占用资源多,需要延时加载。
以上就是单例模式的学习。