随着我们编写代码的深入,我们或多或少都会接触到设计模式,其中单例(Singleton)模式应该是我们耳熟能详的一种模式。本文将比较特别的介绍一下Java设计模式中的单例模式。
10年积累的成都网站设计、成都网站制作、外贸网站建设经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先制作网站后付款的网站建设流程,更有涞水免费网站建设让你可以放心的选择与我们合作。
概念
单例模式,又称单件模式或者单子模式,指的是一个类只有一个实例,并且提供一个全局访问点。
实现思路
其中上面的单例的实例可以有以下几种创建形式,每一种实现都需要保证实例的***性。
饿汉式
饿汉式指的是单例的实例在类装载时进行创建。如果单例类的构造方法中没有包含过多的操作处理,饿汉式其实是可以接受的。
饿汉式的常见代码如下,当SingleInstance类加载时会执行
- private static SingleInstance sInstance = new SingleInstance();
初始化了***的实例,然后getInstance()直接返回sInstance即可。
- public class SingleInstance {
- private static SingleInstance sInstance = new SingleInstance();
- private SingleInstance() {
- }
- public static SingleInstance getInstance() {
- return sInstance;
- }
- }
饿汉式的问题
懒汉式
懒汉式指的是单例实例在***次使用时进行创建。这种情况下避免了上面饿汉式可能遇到的问题。
但是考虑到多线程的并发操作,我们不能简简单单得像下面代码实现。
- public class SingleInstance {
- private static SingleInstance sInstance;
- private SingleInstance() {
- }
- public static SingleInstance getInstance() {
- if (null == sInstance) {
- sInstance = new SingleInstance();
- }
- return sInstance;
- }
- }
上述的代码在多个线程密集调用getInstance时,存在创建多个实例的可能。比如线程A进入null == sInstance这段代码块,而在A线程未创建完成实例时,如果线程B也进入了该代码块,必然会造成两个实例的产生。
synchronized修饰方法
使用synchrnozed修饰getInstance方法可能是最简单的一个保证多线程保证单例***性的方法。
synchronized修饰的方法后,当某个线程进入调用这个方法,该线程只有当其他线程离开当前方法后才会进入该方法。所以可以保证getInstance在任何时候只有一个线程进入。
- public class SingleInstance {
- private static SingleInstance sInstance;
- private SingleInstance() {
- }
- public static synchronized SingleInstance getInstance() {
- if (null == sInstance) {
- sInstance = new SingleInstance();
- }
- return sInstance;
- }
- }
但是使用synchronized修饰getInstance方法后必然会导致性能下降,而且getInstance是一个被频繁调用的方法。虽然这种方法能解决问题,但是不推荐。
双重检查加锁
使用双重检查加锁,首先进入该方法时进行null == sInstance检查,如果***次检查通过,即没有实例创建,则进入synchronized控制的同步块,并再次检查实例是否创建,如果仍未创建,则创建该实例。
双重检查加锁保证了多线程下只创建一个实例,并且加锁代码块只在实例创建的之前进行同步。如果实例已经创建后,进入该方法,则不会执行到同步块的代码。
- public class SingleInstance {
- private static volatile SingleInstance sInstance;
- private SingleInstance() {
- }
- public static SingleInstance getInstance() {
- if (null == sInstance) {
- synchronized (SingleInstance.class) {
- if (null == sInstance) {
- sInstance = new SingleInstance();
- }
- }
- }
- return sInstance;
- }
- }
volatile是什么
Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量 时,另外一个线程能读到这个修改的值。使用volatile修饰sInstance变量之后,可以确保多个线程之间正确处理sInstance变量。
关于volatile,可以访问深入分析Volatile的实现原理了解更多。
利用static机制
在Java中,类的静态初始化会在类被加载时触发,我们利用这个原理,可以实现利用这一特性,结合内部类,可以实现如下的代码,进行懒汉式创建实例。
- public class SingleInstance {
- private SingleInstance() {
- }
- public static SingleInstance getInstance() {
- return SingleInstanceHolder.sInstance;
- }
- private static class SingleInstanceHolder {
- private static SingleInstance sInstance = new SingleInstance();
- }
- }
关于这种机制,可以具体了解双重检查锁定与延迟初始化
好奇问题
真的只有一个对象么
其实,单例模式并不能保证实例的***性,只要我们想办法的话,还是可以打破这种***性的。以下几种方法都能实现。
单例可以继承么
单例类能否被继承需要分情况而定。
可以继承的情况
当子类是父类单例类的内部类时,继承是可以的。
- public class BaseSingleton {
- private static volatile BaseSingleton sInstance;
- private BaseSingleton() {
- }
- public static BaseSingleton getInstance() {
- if (null == sInstance) {
- synchronized(BaseSingleton.class) {
- if (null == sInstance) {
- sInstance = new BaseSingleton();
- }
- }
- }
- return sInstance;
- }
- public static class MySingleton extends BaseSingleton {
- }
- }
但是上面仅仅是编译和执行上允许的,但是继承单例没有实际的意义,反而会变得更加事倍功半,其代价要大于新写一个单例类。感兴趣的童鞋可以尝试折腾一下。
不可以继承的情况
如果子类为单独的类,非单例类的内部类的话,那么在编译时就会出错Implicit super constructor BaseSingleton() is not visible for default constructor. Must define an explicit constructor
,主要原因是单例类的构造器是private,解决方法是讲构造器设置为可见,但是这样做就无法保证单例的***性。所以这种方式不可以继承。
总的来说,单例类不要继承。
单例 vs static变量
全局静态变量也可以实现单例的效果,但是使用全局变量无法保证只创建一个实例,而且使用全局变量的形式,需要团队的约束,执行起来可能会出现问题。
关于GC
因为单例类中又一个静态的变量持有单例的实例,所以相比普通的对象,单例的对象更不容易被GC回收掉。单例对象的回收应该发生在其类加载器被GC回收掉之后,一般不容易出现。
网站题目:谈谈Android Java语言中单例这种设计模式
分享链接:http://www.shufengxianlan.com/qtweb/news26/481626.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联