目录
- 1.单例设计模式的设计思想与应用场景
- 2.单例模式的写法
- 2.1单例模式的饿汉式
- 2.2单例模式的懒汉式
- 2.2.1 懒汉式普通写法
- 2.2.2懒汉式同步代码写法
- 2.2.3懒汉式同步方法写法
- 2.2.4单例模式懒汉式双重校验锁
- 2.3内部类
- 2.4枚举
- 总结
1.单例设计模式的设计思想与应用场景
设计目的
避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。
设计思想
(1)不允许其他程序用new对象: 因为new就是开辟新的空间,在这里更改数据只是更改的所创建的对象的数据,如果可以new的话,每一次new都产生一个对象,这样肯定保证不了对象的唯一性。
(2)在该类中创建对象: 因为不允许其他程序new对象,所以这里的对象需要在本类中new出来
(3)对外提供一个可以让其他程序获取该对象的方法
应用场景
一些常用的工具类、线程池、缓存,数据库,数据库连接池、账户登录系统、配置文件等程序中可能只允许我们创建一个对象。
2.单例模式的写法
单例模式的写法大的方面可以分为5种五种
1.懒汉式
2.饿汉式
3.双重校验锁
4.静态内部类
5.枚举
2.1单例模式的饿汉式
public class Singleton { private static Singleton instance=new Singleton(); //构造器私有化,本身不可创建 private Singleton(){ } //定义一个获取对象的方法 public static Singleton getInstance(){ return instance; } }
饿汉式的静态代码块写法
public class Singleton{ private static Singleton instance = null; static{ instance = new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return instance; } }
备注:两种写法的主要区别在于加载的时间点上,前者在于静态常量显示初始化之后,后者在与静态代码块加载运行的时候。
优点:饿汉js式的写法主要的优势在于实现方式简单,在类加载的时候完成了对象的实例化,因此也可以有效避免线程问题。
缺点:可能造成资源浪费的情况,由于对象实例是在代码加载过程中进行的,只要代码成功加载对象实例就会被创建导致内存浪费。【被占用的内存是非常小的,所以也是推介可以使用的。】
2.2单例模式的懒汉式
2.2.1 懒汉式普通写法
线程不安全,不推荐使用
public class Singleton { private static Singleton instance=null; private Singleton() {}; public static Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
与饿汉式的写法相比较,懒汉式主要是在对**getInstance(android)**方法调用的时候进行实例化对象的操作,既用之再造,节省了内存,故因此称之为懒汉式。
懒汉式的普通写法式存在线程安全问题的,总的来说也就是存在重复创建对象的风险。假设线程1进来,通过if(instance==null)的条件判断准备运行”insatance=new Singleton()“这条代码前,又有线程2运行,此时还没有成功进行Singleton对象实例的创建,if 的条件判断仍然成立,线程2也会去执行instance=new Singleton()这条语句,这样就会导致多个Singleton对象被创建。这就是为什么懒汉式的普通写法会造成线程安全的原因。
2.2.2懒汉式同步代码写法
【给创建对象的语句加锁】与2.2.1的写法类似仍为线程不安全的写法,不推荐使用
public class Singleton7 { private static Singleton instance=null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { instance = new Singleton(); } } return instancwww.devze.come; } }
与2.2.1相类似,synchronized (Singleton.class) 锁住的只是对于对象的创建,仍有有可能再线程1获得”钥匙“还未进行对象的创建的时候,线程二也通过条件判断进行锁等待,一旦获得“钥匙”也会进行对象创建,也是会导致多对象的创建。
2.2.3懒汉式同步方法写法
线程安全,但效率非常低,不推荐使用
public class Singleton { private static Singleton instance=null; private Singleton() {}; public static synchronized Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步,方法进行同步效率太低。
2.2.4单例模式懒汉式双重校验锁
推介使用
public class Singleton { /** * 懒汉式变种,属于懒汉式中最好的写法,保证了:延迟加载和线程安全 */ private static Singleton instance=null; private Singleton() {}; public static Singleton getInstance(){ if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
通过双层if(insatance==null)的条件限制,防止了多线程运行就算在解锁条件下多对象的创建。
通过synchroni编程客栈zed 保证了线程安全,但再第一次对象成功创建后,就可以通过return instance 避免了加锁代码的重复运行,大大提升了效率。
在线程安全以及效率之间找到了一个平衡点。
2.3内部类
public class Singleton{ private Singleton() {}; private static class SingletonHolder{ private static Singleton instance=new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.instance; } }
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同
的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时
并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是
无法进入的。
优点:避免了线程不安全,延迟加载,效率高。
2.4枚举
public enum SingletonEnum { instance; private SingletonEnum() {} public void method(){ } }
也就是直接将枚举类Enum直接当作一个上述例子中的Singleton所对应的实例去进行使用,可以直接通过SingletonEnum.instance.method();的方式对实例中我们要使用的方法进行调用。
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过,这种方式也是最好的一种方式,如果在开发中JDK满足要求的情况下建议使用这种方式。
总结
对于写法的选择,开源项目也是采用的单例模式懒汉式双重校验锁这种写法,其实最安全的写法是枚举,它的实现非常简单而且最安全可谓很完美,但是可能是因为支持的JDK版本有限又或者是因为枚举大家不熟悉所以目前使用的人并不多,可以更多的进行尝试。
另外当我们使用反射机制时可能不能保证实例的唯一性,但是枚举始终可以保证唯一性【这与枚举类在初始类加载的过程以及进行的操作有关】。
到此这篇关于Java设计模式之单例模式Singleton Pattern详解的文章就介绍到这了,更多相关Java单例模式Singleton Pattern内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多js支持编程客栈(www.devze.com)!
精彩评论