一、单例模式的使用场景
1.windows 下的任务管理器以及回收站,整个系统中维护着一个实例
2.网站的计数器,用来达到数据的同步
3.web应用配置对象的读取,使得配置文件成为共享资源
4.数据库连接池对象,主要是节省打开或者关闭连接所引起的效率损耗
5.多线程的线程池设计,使得能够控制线程,方便资源之间的互相通信
6.HttpApplication ,所有的httpmodule 都共享HttpApplication 这一个实例
总结:以上场景都是为了解决两个问题,第一、资源共享 ,第二、资源互相通信
二、单例模式在java中的各种实现
1.懒汉模式,顾名思义,就是在用到这个实例的时候才去加载该对象
注意(1):代码中创建实例过过程中用到了 synchronized 关键字加锁,目的是解决多线程同时操作时线程安全的问题,双层检查判断实例是否为空,保证多个线程取到的是同一个实例。
注意(2):构造方法中判断实例不为空则抛出异常,是为了防止通过反射的方式获取实例。
public class SingletonDemo { //懒汉模式实现 private static SingletonDemo singletonDemo=null; public static SingletonDemo getSingleton(){ if(null==singletonDemo){ try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (SingletonDemo.class){ if(null==singletonDemo){ singletonDemo=new SingletonDemo(); } } } return singletonDemo; } private SingletonDemo() { if(singletonDemo!=null){ throw new RuntimeException("单例不允许多个实例"); } } public static void main(String[] args) { new Thread(()-> { SingletonDemo demo1=SingletonDemo.getSingleton(); System.out.println(demo1); }).start(); new Thread(()-> { SingletonDemo demo2=SingletonDemo.getSingleton(); System.out.println(demo2); }).start(); } }
2.饿汉模式,顾名思义,就是在用到实例之前就已经创建好了实例,使用静态资源加载的方式实现
注意(1):饿汉模式同样会遇到反射方式获取实例,同样也是在构造方法中使用抛出异常的方式
注意(2):饿汉模式有一个小的缺点就是,当没有用的实例的时候可能已经生成了实例,对资源造成了浪费,用到可能二字是因为,当调用到类中其他的静态资源的时候,类就是加载实例,如使用到了main函数,所以静态资源实现实例的初始化无外乎也是通过构造方法生成的实例。
注意(3):使用饿汉模式,静态资源的方式,这种方式是线程安全的,是jvm内部保证的线程安全,无需再我们自己去实现。
public class HungrySingletonDemo { private HungrySingletonDemo() { System.out.println("进入构造"+hungrySingletonDemo); } private static HungrySingletonDemo hungrySingletonDemo=new HungrySingletonDemo(); public static HungrySingletonDemo getSingleton(){ return hungrySingletonDemo; } public static void main(String[] args) { HungrySingletonDemo demo1=HungrySingletonDemo.getSingleton(); HungrySingletonDemo demo2=HungrySingletonDemo.getSingleton(); System.out.println(demo1==demo2); } }
3.内部类,获取的唯一实例是通过内部类的方式实现实例初始化
注意(1):代码中设计到对象写入流的操作,是为了验证发序列化方式获取的实例不唯一性,readResolve函数是为了反序列化获取时,是我们自己定义的实例,保证发序列化拿到的对象和内部内中生成的一致。
注意(2):内部类的方式同样会涉及到反射获取,同样也是在构造方法中返回异常限制
注意(3):内部类的方式同样也是线程安全的,在jvm中保证
public class InnerSingletonDemo implements Serializable{ private static final long serialVersionUID = 5754104541168322017L; private InnerSingletonDemo(){ System.out.println("进来了"); } private static class InnerTestDemo{ private static final InnerSingletonDemo demo=new InnerSingletonDemo(); } public static InnerSingletonDemo getSingleton(){ return InnerTestDemo.demo; } public static void main(String[] args) throws IOException, ClassNotFoundException { /*InnerSingletonDemo demo1=InnerSingletonDemo.getSingleton(); InnerSingletonDemo demo2=InnerSingletonDemo.getSingleton();*/ InnerSingletonDemo demo1=InnerSingletonDemo.getSingleton(); // ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("outSerializable")); //oos.writeObject(demo1); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("outSerializable")); InnerSingletonDemo object=(InnerSingletonDemo)ois.readObject(); System.out.println(object); System.out.println(demo1); // System.out.println(demo1); } Object readResolve() throws ObjectStreamException{ return InnerTestDemo.demo; }
4.枚举,枚举enum也是一个对象,其中的枚举量就是一个实例
注意(1):枚举的方式在序列化获取实例的过程中是默认不可以的,所以通过反序列化的方式获取实例的方式是不允许的
public enum EunmSingletonDemo { SYSTEM("1","系统消息"); private String type; //类型 private String desc; //描述 EunmSingletonDemo(String type, String desc) { this.type=type; this.desc=desc; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public void print(){ System.out.println(this.hashCode()); } } class EnumTest{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /* Constructor<EunmSingletonDemo> constructor=EunmSingletonDemo.class.getDeclaredConstructor(String.class,int.class); EunmSingletonDemo object=(EunmSingletonDemo)constructor.newInstance("INSTANCE",0);*/ EunmSingletonDemo instance=EunmSingletonDemo.SYSTEM; EunmSingletonDemo instance2=EunmSingletonDemo.SYSTEM; // EunmSingletonDemo instance3=new EunmSingletonDemo("zhangsan","lisi"); System.out.println(instance.getDesc()+"--"+instance2.getDesc()); } }