简要说明
1、单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使
用单例模式可以提高系统性能
2、当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
3、单例模式 使用的场景:需要 频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、 工具类对象、频繁访问数据库或文件的对象(比如 数据源、session 工厂等)
1、饿汉式单例模式
package singleton; /** * 饿汉式单例模式 */ public class Singleton { //方法区静态常量池初始化对象 private static final Singleton singleton = new Singleton(); //构造方法私有化,不让外部new private Singleton(){} public static Singleton getInstance(){ return singleton; } public void m(){ System.out.println("m"); } public static void main(String Args[]){ Singleton p1 = Singleton.getInstance(); Singleton p2 = Singleton.getInstance(); System.out.println(p1 == p2); } }
饿汉式在类进行初始化的时候就将类实例创建好,不管是否使用。而在使用时直接调用就行,不需要进行判断,同时需要花费大量的空间,典型的空间换时间。
2、懒汉式单例模式
必须加锁,如果不加锁,使用懒汉式的多线程不安全
1,在对象之间加锁,缺点是每个对象都需要进行等待和判断,效率低
package singleton; /** * 懒汉式:用的时候进行初始化 */ public class Singleton2 { //不需要final,在加final时需要初始化 private static Singleton2 singleton2; private Singleton2(){} public static synchronized Singleton2 getInstance(){ //在此区间可能出现线程不安全 //因此需要加锁使得线程同步,缺点是效率降低 if(singleton2 == null){ try{ Thread.sleep(10); }catch(Exception e){ e.printStackTrace(); } singleton2 = new Singleton2(); } return singleton2; } public void m(){ System.out.println("m"); } public static void main(String[] args) throws Exception{ for (int i = 0; i < 100; i++) { new Thread(()->{ //从打印区间看看对象的hash码是否相同 System.out.println(Singleton2.getInstance().hashCode()); }).start(); } } }
2,改进,使用双重检查
package singleton; public class Singleton3 { //不需要final,在加final时需要初始化 private static Singleton3 singleton3; private Singleton3(){} public static Singleton3 getInstance(){ //在此区间可能出现线程不安全 //因此需要加锁使得线程同步,缺点是效率降低 if(singleton3 == null){ //因此需要双重检查 synchronized (Singleton3.class){ if(singleton3 == null){ try{ Thread.sleep(10); }catch(Exception e){ e.printStackTrace(); } singleton3 = new Singleton3(); } } } return singleton3; } public void m(){ System.out.println("m"); } public static void main(String[] args) throws Exception{ for (int i = 0; i < 100; i++) { new Thread(()->{ //从打印区间看看对象的hash码是否相同 System.out.println(Singleton3.getInstance().hashCode()); }).start(); } } }
3,使用比较完美的静态内部类实现单例模式
package singleton; /** * 完美的写法 */ public class Singleton4 { private Singleton4(){} //使用静态内部类 private static class Singleton4Hold{ private final static Singleton4 singleton4 = new Singleton4(); } public static Singleton4 getInstance(){ return Singleton4.getInstance(); } public void m(){ System.out.println("m"); } public static void main(String[] args) throws Exception{ for (int i = 0; i < 100; i++) { new Thread(()->{ //从打印区间看看对象的hash码是否相同 System.out.println(Singleton4.getInstance().hashCode()); }).start(); } } }
4,枚举实现单例模式
简单高效,不仅可以解决线程同步的问题,还可以防止被序列化
防止被序列化的原因是因为枚举内部没有构造方法
package singleton; public enum Singleton5 { INSTANCE; public void m(){} public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(Singleton5.INSTANCE.hashCode()); }).start(); } } }
5、总结
-单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉
-单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式