单例模式

Posted xiejiangping

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。

单例模式:只有一个实例的设计模式。
单例模式有三点要求

    *  构造方法私有化(保证外部不能直接构造)

    *  提供一个静态私有属性指向实例

    * 提供一个公有的静态方法提供实例

简单单例演示 

 1 /**
 2  * 懒汉式单例模式演示
 3  * @author 67471
 4  *
 5  */
 6 public class Singeton {
 7     private static Singeton singeton;
 8     private Singeton(){}
 9     private static Singeton getSingeton(){
10         if (singeton==null) {
11             singeton =new Singeton();
12             return singeton;
13         }
14         return singeton;
15     }
16   }

测试

public static void main(String[] args) {
        Singeton instance = Singeton.getSingeton();
        Singeton instance2 = Singeton.getSingeton();
        System.out.println(instance==instance2);
        
    }

结果:true

这里还有一个问题就是多线程下就有问题了。什么问题呢?请看下面代码

演示

 1 package thead;
 2 /**
 3  * 多线程情况下的单利模式
 4  * @author 67471
 5  *
 6  */
 7 public class Singeton implements Runnable{
 8     private static Singeton singeton;
 9     private Singeton(){}
10     private static Singeton getSingeton() throws InterruptedException{
11         if (singeton==null) {
12             Thread.sleep(200);
13             singeton =new Singeton();
14             return singeton;
15         }
16         return singeton;
17     }
18     @Override
19     public void run() {
20         try {
21             System.out.println(getSingeton().hashCode());
22         } catch (InterruptedException e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         }
26     }
27 
28 }

 

 

测试

    public static void main(String[] args) {
        Thread[] thrs = new Thread[10];
        for (int i = 0; i < thrs.length; i++) {
            thrs[i] = new Thread(new Singeton3());
        }
        for (int i = 0; i < thrs.length; i++) {
            thrs[i].start();
        }
        
    }

结果:

935563448
1320194849
1820973978
1202453864
935563448
139607202
1326101490
935563448
935563448
763347431

  产生这种结果的原因是每个线程进去后判断singeton为null,都创建了一个实例。拿到的不是同一个实例,那就不是单利模式。

解决这个问题的思路:用synchronized关键字解决,问题是synchronized加在哪?加在getSingeton()方法上会解决这个问题,但是效率太低,浪费系统资源,如果属性singeton不为null,将其他线程挡在

外面不合适,应该在创建对象时同步。看下面演示

还是上面代码只改变getSingeton方法

 1 private static Singeton3 getSingeton() throws InterruptedException{
 2         if (singeton==null) {
 3             Thread.sleep(200);
 4             synchronized(new Object()){
 5                 singeton =new Singeton();
 6                 return singeton;
 7             }
 8         }
 9         return singeton;
10 }

测试结果

139607202
1175759956
1414135615
512965639
763347431
763347431
1820973978
1320194849
1202453864
139607202

这里就MMP了,想了想漏了一个情况,多个线程同时进来时,此时singeton=null。都进入if循环内,都准备进入同步代码块内,如果不再次判断还是会创建多个实例

应该在同步代码块中再判断下是否为null,是null再创建,如下;

 1 private static Singeton3 getSingeton() throws InterruptedException{
 2         if (singeton==null) {
 3             Thread.sleep(200);
 4             synchronized(new Object()){
 5                 
 6                 if (singeton==null) {
 7                     
 8                     singeton =new Singeton();
 9                     return singeton;
10                 }
11             }
12         }
13         return singeton;
14     }

测试结果

139607202
139607202
139607202
139607202
139607202
139607202
139607202
139607202
139607202
139607202

 

总结:这就是单利模式下的双重检测,懂了之后抑郁症也好了。

 



以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

性能比较好的单例写法

片段作为 Android 中的单例

单例片段或保存网页视图状态

你熟悉的设计模式都有哪些?写出单例模式的实现代码

单例模式以及静态代码块