夹江企业网站建设报价,阿里云网站建设 部署与发布笔记,wordpress简单广告框修改,活动策划书模板单例模式简介
90%以上的设计模式都或多或少的应用了接口和抽象类#xff0c;而单例比较特殊#xff0c;并没有接口的应用。 单例Singleton指仅仅被实例化一次的类。通常被用来代表那些本质上唯一的系统组件。————《Effective Java》 数据库连接获取类的对象可以是单例的…单例模式简介
90%以上的设计模式都或多或少的应用了接口和抽象类而单例比较特殊并没有接口的应用。 单例Singleton指仅仅被实例化一次的类。通常被用来代表那些本质上唯一的系统组件。————《Effective Java》 数据库连接获取类的对象可以是单例的。
单例的意思就是在内存中只有一个对象。而单例和static有是有区别的static是用来修饰类中的成员变量和成员方法的而单例则属于对象的层面。
单例的实现
思考如何在内存中只有一个对象——不能让外界随意的实例化new。封装思想中有一个叫“属性私有化方法公开化”的概念在需要单例的类中自己去实例化。这就是单例的两个条件不能被外界实例化根据封装的特征属性私有化方法公开化。
外界不能实例化实现的方式就是将需要设置为单例的类以下简称单例类的构造器设置为private。
注意如果电脑上安装了多个虚拟机的话那么实际上并不是真正意义上的单例而是每个虚拟机会有一个单例。
懒汉单例模式
懒汉单例模式在第一次使用对象的时候才会去创建对象但最简单的懒汉单例存在多线程安全问题依然有可能创建多个实例。
/*** 懒汉单例模式Demobr懒汉单例是在运行时调用的一种行为。* 单例模式Singleton Pattern是 Java 中最简单的设计模式之一。br* 这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式。br* 这种模式涉及到一个单一的类该类负责创建自己的对象同时确保只有单个对象被创建。br* 这个类提供了一种访问其唯一的对象的方式可以直接访问不需要实例化该类的对象。br* * 类名LazySingletonbr* 作者 mhtbr* 日期 2018年3月18日-下午8:41:01br*/
public class LazySingleton {/** 属性私有化 */private static LazySingleton instance null;/** 构造器私有化 */private LazySingleton() { }/** 公开获取单例对象 */public static LazySingleton getInstance() {if (instance null) {instance new LazySingleton();}return instance;}
}
饿汉单例模式
饿汉单例会在类加载的时候即实例化对象这与懒汉单例的创建时机截然不同不存在线程安全问题
但缺点是需要提前占用内存资源。
/*** 类名EagerSingletonbr* 作者 mhtbr* 日期 2018年3月18日-下午8:41:01br*/
public class EagerSingleton {/** 使用静态常量*/private static final EagerSingleton instance new EagerSingleton();private EagerSingleton() {System.out.println(这是构造器LazySingleton());}public static EagerSingleton getInstance() {return instance;}public void doSomething() {System.out.println(EagerSingleton is doing something now ...);}
}懒汉饿汉静态内部类
该方法通过私有静态内部类来实现单实例延迟加载因为静态内部类不会因为外部类的加载而加载所以可以在第一次使用静态类的时候才执行加载。 优点是不仅可以达到懒汉式的延迟加载使类的加载速度提升避免一开始占用过多内存又具备线程安全性无需判断性能上也会更快一些。
/*** 作者 mhtbr* 日期 2018年3月19日-下午9:55:52br*/
public class Singleton {private Singleton () {System.out.println(Singleton...);}private static class SingletonInstance {private static final Singleton s new Singleton();}public static Singleton getInstance() {return SingletonInstance.s;}
}
双重检查-单例模式线程安全的懒汉式单例
双重检查单例模式 使用双重检查同步延迟加载来创建单例的做法是一个非常优秀的做法 其不但保证了单例线程安全而且切实提高了程序的运行效率。
实现原理
Java语言提供了一种较synchronized稍弱的同步机制volatile用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后编译器与运行时都会注意到这个变量是共享的因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方因此在读取volatile类型的变量时总会返回最新写入的值。
getInstance()方法描述
为了在保证单例的前提下提高运行效率我们需要对单例对象dcs进行第二次检查目的是避开过多的同步因为这里的同步只需在第一次创建实例时才同步一旦创建成功以后获取实例时就不需要同步获取锁了。这种做法无疑是优秀的但是我们必须注意一点必须使用volatile关键字修饰单例对象。
public class DoubleCheckSingleton {// 使用volatile关键字防止重排序因为 new Instance()是一个非原子操作。private static volatile DoubleCheckSingleton dcs;private DoubleCheckSingleton() {}public static DoubleCheckSingleton getInstance() {if (dcs null) {synchronized (DoubleCheckSingleton.class) {// 只需在第一次创建实例时才同步if (dcs null) {dcs new DoubleCheckSingleton();}}}return dcs;}
}
注册单例模式
public class RegisterSingletonT {/* 注册表 */private static MapString, Object regMap new HashMap();// 类加载过程中静态初始化static{Connection conn new Connection();UserService us new UserService();// 初始化注册表regMapregMap.put(conn.getClass().getName(), conn);regMap.put(us.getClass().getName(), us);}private RegisterSingleton() { }public synchronized static Object getInstance(String key) {if (key null) return null;try {if (regMap.get(key) null) {// 这里注意此Demo是以类名作为key传入map中的因此这里的传值也是相关方法通过类名找到类然后在进行自动实例化regMap.put(key, Class.forName(key).newInstance());}return regMap.get(key);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
}测试
package design.pattern;public class testSingleton {public static void main(String[] args) {UserService us (UserService) RegisterSingleton.getInstance(UserService.class.getName());Connection conn1 (Connection) RegisterSingleton.getInstance(Connection.class.getName());Connection conn2 (Connection) RegisterSingleton.getInstance(Connection.class.getName());System.out.println(us);System.out.println(conn1);System.out.println(conn2);System.out.println(conn1 conn2);}
}
运行结果
design.pattern.UserService15db9742
design.pattern.Connection6d06d69c
design.pattern.Connection6d06d69c
true
对单例模式应用的理解
在spring框架中大多数对象的使用都是单例模式的比如获取用户User对象的方法类UserDao和UserService都是单例的包括controller他们默认都单例的。因为实际上我们真正需要多个对象的是User而并不是获取User的方法类。
这也就充分解释了Spring注解中的Autowired自动注入对象的实现方式。细心的我们也都可以发现这些自动注入的对象一般都是实现某种业务的中间类对象而并不是最终我们需要的对象因此为了避免频繁的创建这些“中间件”对象占用不必要的内存空间都是以单例的形式来创建的。另外数据库连接对象Connection也一定是单例的。
单例模式的误解
单例会导致线程阻塞吗不会的。
举个生活中的例子饭店只有一个菜谱但是有多个厨师多个厨师共享这份菜谱并同时做一道菜彼此之间是没有影响的老师在黑板上写下一个数学题同学们在下面计算结果黑板就是内存数学题就是单例对象同学们就是各个线程计算过程之间是没有影响的。
单例模式的优点
1.内存中只有一个对象节省内存空间
2.避免频繁的创建和销毁对象可以提高性能
3.避免对共享资源的多重占用简化访问
4.为整个系统提供一个全局访问点
单例模式的应用场景
在计算机系统中线程池、缓存、日志对象、对话框、打印机、显卡驱动程序对象常被设计为单例的。实际上这些应用都或多或少具有资源管理器的功能。
其核心在于为整个系统提供一个唯一的实例其应用场景包括但不仅限于一下几种
1.有状态的工具类
2.频繁访问数据库或文件的对象
参考
《彻头彻尾理解单例模式以及其在多线程环境中的应用》