单例模式

GOF中最简单的一个模式,只涉及到一个类。比如线程池、缓存等这些对象在应用只能被实例化一次,如果实例化多次,有可能会造成不可预测的后果。何谓单例?答曰:该类的对象只能被实例化一次。


程序猿:如果只是为了让对象被创建一次,那我们直接把对象放在全局变量在不就可以解决这类问题了?
大师:放在全局中的确可以解决该类问题,但还是存在一些缺陷。
程序猿:什么缺陷?
大师:你把对象放在全局变量中,意味着在程序启动的时候就必须要实例化,如果该对象创建的时间比较长而且
在创建出来的时候不一定就用得上,那就会消耗资源,全局静态变量的确是能解决问题,但单例模式也一个不错
的选择,你可以在需要的时候再创建它。

单例模式实现



      类如何被实现化 :    new MyInstance(); 如果另一个对象想创建怎么办?当然还是可以通过new 来再一次进行创建。只要是公共类,就可以被重复创建。那如果不是公共类又是一种什么情况呢?只有同一个包中的类可以实例化它,但是也可以被多次实例化。
      如果有多年经验的你可能会想到 private MyInstance(),对了,如果应用私有构造函数,那就不能通过
new来随心所欲的进行实例化了。那你可能又有一个问题要问了,不能通过new来实例化对象,那通过什么方式来实例化对象?来看一下下面的代码你就会晃然大悟了。

1 public class MyInstance{
2 
3        private MyInstance(){
4        }
5        //
6        public static MyInstance getMyInstance(){
7               return new MyInstance();
8        }
9 }
View Code

通过getMyInstance方法,就可以进行实例化了,但这个跟直接跟通过new创建有何区别呢,代码还没有完成,我们来进一步完善

public class MyInstance{

      private static MyInstance instance;
      //通过私有构造函数,防止用户通过默认的构造函数来实例化对象
      private MyInstance(){}  
      public static MyInstance getMyInstance(){
             if(instance==null){
System.out.print("myinstance is null"); instance
=new MyInstance();
}
return instance; } }

下面代码测试一下是否工作正常,建一个类来实例化MyInstance,然后在main中调用,

1 public class Test {
2     public void business(){
3         MyInstance _instand1 = MyInstand.getMyInstance();
4         MyInstance _instand2 = MyInstand.getMyInstance();
5     }
6 }
View Code
1 public class Main {
2 
3     public static void main(String[] args) {
4          //TODO Auto-generated method stub
5          Test t = new Test();
6          t.business();
7     }
8 
9 }
View Code

代码执行后,你会看到控制台只输出一条“Myinstance is null“,说明MyInstance只被实例化了一次。到此单例模式基本完成,为什么是基本完成而不是已完成了呢。要知详情请看后文。

得意得太早


 现实永远比想象残酷,当你以为上面的代码已完成交给你的其他猿类使用时,末日来临了。请猿们看下面的代码,看看将会发生什么。

 1 public class Test extends Thread{
 2 
 3     @Override
 4     public void run() {
 5         // TODO Auto-generated method stub
 6               super.run();
 7                   MyInstance _instance = MyInstanece.getMyInstance();
 8     }
 9 }
10 
11 
12 public class Main {
13 
14     /**
15      * @param args
16      */
17     public static void main(String[] args) {
18         // TODO Auto-generated method stub
19          Test t1 = new Test();
20          t1.start();
21          Test t2 = new Test();
22          t2.start();
23     }
24 
25 }
View Code

执行以上代码发现了什么?出现了两条“myinstand is null”,你测试的没事,是出现两次,这就是线程做得手脚。

解决


既然是线程引起的,那我们就解决线程问题,在MyInstance的getMyInstance方法前加上“synchronized”,让线程同步,这种灾难将得到解决

 1 public class MyInstance {
 2     private static MyInstance _instance;
 3     private MyInstance(){}
 4     public static synchronized MyInstand getMyInstance(){
 5         if(_instance==null){
 6             System.out.print("myinstand is null");
 7             _instance =new MyInstance();
 8         }
 9         return _instance;
10     }
11 }
View Code

现在再执行代码,发现只实例化了一次。

一波未平一波又起


通过线程同步解决了上面出现的问题,但新的问题又出现在了我们的眼前,那就是程序的性能降低了。--未完待续

原文地址:https://www.cnblogs.com/sunrfun/p/3635211.html