有些类需要计划生育-----单例模式
单例模式应该是工作中最常见的一种设计模式,不会针对一种语言来讲解设计模式,因为设计模式是一种编程思想,它不应该被固定在某个语言上。
为什么要用单例模式:在实际工作中,我们可能要做一个简单的缓存,一个连接池,或者只需要一个实例对象的类比如说工具类的对象我们想它在用到的时候去实例,那么我们就需要了单例模式。
单例模式(singleton): 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
我们先来写一个简单的singleton的类:
/** * 单线程下一个简单的单例模式 * @author GMZ * */ public class Singleton_1 { //声明一个私有的自己的对象 private static Singleton_1 singleton; //私有化自己的构造方法 让外部无法通过构造器去实例本类的对象 private Singleton_1(){} //提供一个可供全局访问的访问点 //这个方法必须是静态的 因为如果不是静态方法的话你对象无法创建这个类就无法调用,要static去修饰然后让这个方法属于类 public static Singleton_1 getSingleton(){ //我们要判断这个对象是否已经创建过了,如果是创建过的那就直接返回。 if(singleton == null){ singleton = new Singleton_1(); } return singleton; } }
这是一个在单线程下可以完美执行的单例类,为什么说它是在单线程下,因为在多线程下 if(singleton == null)这块代码可能会出现竞态条件,导致对象的创建可能不是单例的,是多个。
那么如何在一个多线程的环境中去让一个单例的类,不会出现数据安全的问题呢,
常用的解决办法有三个
第一个是在上面这个原有的类上加一个锁(我在这贴的是java的代码)
public class Singleton_2 { //声明一个私有的自己的对象 private static Singleton_2 singleton; private static final Object lockObject = new Object(); //私有化自己的构造方法 让外部无法通过构造器去实例本类的对象 private Singleton_2(){} //提供一个可供全局访问的访问点 //加上synchronized关键字解决 public static synchronized Singleton_2 getSingleton(){ //我们要判断这个对象是否已经创建过了,如果是创建过的那就直接返回。 if(singleton == null){ singleton = new Singleton_2(); } return singleton; } //或者是这样 public static Singleton_2 getSingleton1(){ //用synchronized去锁住这个对象,当当前的线程拿到锁只有才可以执行下面代码块中的代码 synchronized(lockObject){ //我们要判断这个对象是否已经创建过了,如果是创建过的那就直接返回。 if(singleton == null){ singleton = new Singleton_2(); } } return singleton; } }
还有一个是静态初始化也就是常说的饿汉
/** * 静态初始化 单例模式 * @author GMZ * */ public class Singleton_3 { //声明一个私有的自己的对象 private static Singleton_3 singleton = new Singleton_3(); //私有化自己的构造方法 让外部无法通过构造器去实例本类的对象 private Singleton_3(){} //提供一个可供全局访问的访问点 public static Singleton_3 getSingleton(){ return singleton; } }
这个模式简介明了,实在类加载的时候就初始化对象并实例,类的加载只会执行一次 所以这个类是绝对安全的 缺点是 没有lazy loading的效果 所以内存使用率降低。
还有最后一个就是双重锁定模式
/** * 双重锁定(双重检查) * @author GMZ * */ public class Singleton_4 { //声明一个私有的自己的对象 private static Singleton_4 singleton; //私有化自己的构造方法 让外部无法通过构造器去实例本类的对象 private Singleton_4(){} //提供一个可供全局访问的访问点 public static Singleton_4 getSingleton(){ //这里检查两次 去确保它的对象创建的安全性 //缺点是当多个线程去调用时,系统的开销比较大,优点是lazy loading 在需要时才创建相对减少了内存的消耗 if(singleton == null){ synchronized (Singleton_4.class) { if(singleton == null){ singleton = new Singleton_4(); } } } return singleton; } }