介绍
单例模式就是采取一定的方法保证在整个系统中对某个类只存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
举例:
hibernate的sessionFactory,原因:sessionFactory充当数据存储代理,负责创建session对象,一般情况下项目只需要一个sessionfactory就够了,这时就会使用单例模式。
实现方式
1、饿汉式
2、懒汉式
3、双重检查
4、静态内部类
5、枚举
1)、饿汉式(静态常量)
实现步骤:
(1)、构造器私有化
(2)、类内部创建对象
(3)、向外暴露一个静态的公有方法
(4)、代码实现
优缺点:
优点:写法简单,就在类装载的时候就完成实例化,避免了线程同步的问题
缺点:在类装载的时候就完成实例化,没有达到lazy loading(延迟加载)的效果。如果全程不需要使用到这个实例,就会造成不必要的内存浪费
代码例子
package singleton;//饿汉式(静态常量)public class SingletonTast01 {public static void main(String[] args) {//4、代码测试Singleton instance= Singleton.getInstance();Singleton instance2= Singleton.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton {//1、构造器私有化private Singleton(){}//2、本类内部创建对象实例private final static Singleton instanace=new Singleton();//3、向外暴露一个静态的公有方法public static Singleton getInstance(){return instanace;}}/***由于该内部类使用了饿汉式模式,因此在定义该类的时候就先在其内部进行了实例化。*因此调用该类的时候都是使用了同一实例。同时也省去了每次调用的是都去new一个实例的工作。所以也两个实例都指向了同一内存地址**/
2)、饿汉式(静态代码块)
实现方式:
此方式的构建与上一种基本一致,请参照上面。
优缺点:
(1)此方式和前面的相同,只是将类实例化放在了静态代码块中,也是在类装载的时候就执行静态代码块中的代码。优缺点和上面的基本相同。
代码例子:
package singleton;//饿汉式(静态常量)public class SingletonTast02 {public static void main(String[] args) {//4、代码测试Singleton02 instance= Singleton02.getInstance();Singleton02 instance2= Singleton02.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton02 {//1、构造器私有化private Singleton02(){}//2、本类内部创建对象实例private final static Singleton02 instanace;//静态代码块static {instanace=new Singleton02();}//3、向外暴露一个静态的公有方法public static Singleton02 getInstance(){return instanace;}}//除了实例化的地方不一样其他的和上面基本相同
(3)、懒汉式(线程不安全)
实现方式
在类中定义一个公有的静态方法,当调用了这个方法才去自动创建实例对象。
优缺点:
起到了延迟加载的作用,但是仅限在单线程下使用。如果在多线程时,一个线程还未来得及自行IF下面的代码时又有线程进来,这时就会产生多个实例。因此不适用于实际开发。
例子
package singleton;//懒汉式public class SingletonTast03 {public static void main(String[] args) {Singleton03 instance=Singleton03.getInstance();Singleton03 instance2=Singleton03.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton03{private static Singleton03 instance;private Singleton03(){}//提供一个静态的公有方法,当使用到该方法是才去创建实例public static Singleton03 getInstance(){if (instance==null){instance=new Singleton03();}return instance;}}
(4)、懒汉式(线程同步,安全)
构建方式
与前面的线程不安全基本一致,就是在构建静态方法时加入了同步锁synchronized
优缺点
解决了线程安全问题。但是就降低了运行效率。每次执行getInstance方法时都要进行同步,但是实际上只需要执行一次实例化就够了。后面需要直接return就可以了,因此使得方法进行同步效率太低了。
在实际开发中不推荐使用此方式。
例子
package singleton;//懒汉式public class SingletonTast04 {public static void main(String[] args) {System.out.println(\"懒汉式,线程同步安全\");Singleton04 instance= Singleton04.getInstance();Singleton04 instance2= Singleton04.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton04 {private static Singleton04 instance;private Singleton04(){}//提供一个静态的公有方法,当使用到该方法是才去创建实例public static synchronized Singleton04 getInstance(){if (instance==null){instance=new Singleton04();}return instance;}}
(5)、双重检查
构建方式
使用懒汉式线程安全,同步方法,同时加入双重检查代码。
优缺点
可以低延时加载,并且解决了线程安全问题,同时提高了效率,双重检查避免了重复进行方法同步执行,提高了效率。推荐实际开发使用。
例子
package singleton;public class SingletonTest05 {public static void main(String[] args) {System.out.println(\"双重检查\");Singleton05 instance= Singleton05.getInstance();Singleton05 instance2= Singleton05.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton05 {private static volatile Singleton05 instance;private Singleton05(){}//提供一个静态的公有方法,加入双重检查public static synchronized Singleton05 getInstance(){if (instance==null){synchronized (Singleton05.class){if (instance==null){instance=new Singleton05();}}}return instance;}}
(6)、静态内部类
构造方式
(1)、构造器私有化
(2)、编写静态内部类
(3)、提供一个静态公有方法
优缺点
采用了类装载机制来保证初始化时只有一个线程;同时在类装载是不会立即被实例化,而是在需要时调用静态内部类的方法。在第一次加载类时初始化,同时JVM帮助我们保证率线程安全。从而避免了线程不安全问题,同时实现了延时加载,效率高。推荐使用。
例子
package singleton;public class SinletonTest06 {public static void main(String[] args) {System.out.println(\"静态内部类实现单例\");Singleton06 instance= Singleton06.getInstance();Singleton06 instance2= Singleton06.getInstance();System.out.println(instance == instance2);System.out.println(\"instance.hashcode:\" + instance.hashCode());System.out.println(\"instance2.hashcode:\" + instance2.hashCode());}}class Singleton06{private static volatile Singleton05 instance;private Singleton06(){}//编写静态内部类private static class SingletonInstance{private final static Singleton06 SINGLETON_06=new Singleton06();}//提供一个静态的公有方法,返回SINGLETON_06public static synchronized Singleton06 getInstance(){return SingletonInstance.SINGLETON_06;}}
(7)、枚举
优缺点
借助于JDK的枚举来实现单例,避免了多线程安全问题,同时还能防止反序列化重新创建对象。受作者极力推崇
例子
package singleton;public class SinletonTest07 {public static void main(String[] args) {Singleton07 singleton=Singleton07.SINGLETON_07;Singleton07 singleton2=Singleton07.SINGLETON_07;System.out.println(singleton == singleton2);System.out.println(singleton.hashCode());System.out.println(singleton2.hashCode());singleton.sayOk();}}//使用枚举enum Singleton07{SINGLETON_07;public void sayOk(){System.out.println(\"ok\");}}
总结
单例模式是一种很经典的设计模式,JDK源码中也多处使用了该设计模式。其中java.lang.Runtime就是很经典的一个单例模式(饿汉式)例子
单例模式保证了系统中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。但是要记住在实例一个单例的时候,必须使用相应的启动方法来获取对象,而不是像平时那样使用new。
适用场景
需要频繁进行创建和销毁的对象;创建对象需要消耗过多资源的(重量级对象,如:工具类对象,数据源对象等)