设计模式-单例模式
单例,顾名思义全局只能有一个实例对象。
基本原理就是
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。1. 使构造方法私有化(不能随便的通过构造方法创建实例来保证单例);
2. 有一个可以获取实例的静态方法(因为自己不可以创建实例,所以该方法必须是静态的,否则无法调用);
3. 有一个静态私有的局部变量指向自己(私有是因为不可外部调用,静态是因为2中的静态方法需要引用)。
单例模式又有饿汉式和懒汉式之分
按名字理解,饿汉式比较急,不管用没用到直接先创建一个实例,好处是快,在需要的时候直接用,坏处是占用资源,因为如果没有用到,它也创建好在那放着:
1 //单例模式-饿汉式-线程安全 2 public class Singleton{ 3 //私有内部类,需要时进行加载 4 private static Singleton singleton = new Singleton(); 5 //构造方法私有化,不允许外部创建 6 private Singleton(){} 7 //获取实例的静态方法,直接调用即可 8 public static Singleton getInstance(){ 9 return singleton; 10 } 11 }
懒汉式比较懒,等到用的时候才会创建出一个实例,好处是不会浪费资源,坏处是初次调用有一个创建的时间:
1 //单例模式-懒汉式-非线程安全 2 public class Singleton{ 3 //私有内部类,需要时进行加载 4 private static Singleton singleton; 5 //构造方法私有化,不允许外部创建 6 private Singleton(){} 7 //获取实例的静态方法,直接调用即可 8 public static Singleton getInstance(){ 9 //判断实例是否为空,如果为空也就是第一次调用则创建一个实例对象 10 if(singleton == null){ 11 singleton = new Singleton(); 12 } 13 return singleton; 14 } 15 }
饿汉式是线程安全的,因为它是在类加载时直接初始化了一个实例,多个线程同时调用也只是这一个实例;而懒汉式则是非线程安全的,因为它是调用的时候才会创建实例,这样就有一个问题,当多个线程假如同时到了判断为空的逻辑时,就会每个线程创建一个实例,这样就违背了单例的规则。
为了保证线程安全我们可以使用synchronized关键字进行方法同步或代码块,建议只同步判断为空的代码块,因为如果同步方法同步的作用域会比较大,锁的粒度比较粗,效率会低。下面同步方法的代码已经注释掉,保留了同步代码块的形式,这样即可保证线程安全。
1 //单例模式-懒汉式-线程安全 2 public class Singleton{ 3 //私有内部类,需要时进行加载 4 private static Singleton singleton; 5 //构造方法私有化,不允许外部创建 6 private Singleton(){} 7 //获取实例的静态方法,直接调用即可 8 /*public static synchronized Singleton getInstance(){ 9 //判断实例是否为空,如果为空也就是第一次调用则创建一个实例对象 10 if(singleton == null){ 11 singleton = new Singleton(); 12 } 13 return singleton; 14 }*/ 15 16 //获取实例的静态方法,直接调用即可 17 public static Singleton getInstance(){ 18 synchronized (Singleton.class) { 19 //判断实例是否为空,如果为空也就是第一次调用则创建一个实例对象 20 if (singleton == null) { 21 singleton = new Singleton(); 22 } 23 } 24 return singleton; 25 } 26 }
但这样因为有同步代码块效率其实还是低,我们可以使用内部类的方式实现:
1 //单例模式-懒汉式-线程安全 2 public class Singleton{ 3 //私有内部类,需要时进行加载 4 private static class Inner { 5 private static Singleton singleton = new Singleton(); 6 } 7 //构造方法私有化,不允许外部创建 8 private Singleton(){} 9 //获取实例的静态方法,直接调用即可 10 public static Singleton getInstance(){ 11 return Inner.singleton; 12 } 13 }
这样既保证了效率(因为是在使用时才进行的实例初始化),也保证了线程安全问题(同饿汉式相似,是在内部类加载时直接进行的初始化)。
到这里单例模式基本已经介绍完,但是需要注意的是:单例模式如果使用反射的方式依然是可以创建多个实例的,因为反射可以拿到私有的构造方法,就可以自己创造实例,所以我们使用单例模式要禁止使用反射进行对象的创建。
