单例模式--导读

  在我们编程过程中我们会经常碰到一些对象,他们要频繁的使用,而我们为了方便于是便频繁的对这个对像进行新建,当然这对我们编程的确是很方便

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

但是,这同时会造成很大的资源浪费。所以我们想出了一个限制资源的方法,那就是只能新建一次,而不允许第二次新建,但是我们又需全局对该类的引用

所以我们干脆使得该类不需要在其他类中进行实例化,可以对该类进行直接引用,这便是单例模式。

单例模式--定义

  从上面的导读我们可以理解到,单例模式的特点为:              

                        1、单例类只能有一个实例。

                        2、单例类必须自己创建自己的唯一实例。

                        3、单例类必须给所有其他对象提供这一实例。

单例模式--模型

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

设计模式之--单例模式 随笔 第1张

单例模式--代码实现

  对于单例模式我们想的方法必然是私有化构造方法,同时使用一个静态变量,让其他类无法新建该对象,这样便可以确保该对象只能产生一个

当要引用时先判断静态变量中是否为空,为空则新建,不为空则不新建,私有化构造方法可以确保其他类不能对该类进行实例化

下面进行代码展示:

Singleton.java单例的类

package Singleton;
//懒汉式单例
public class Singleton {
    
    //定义静态变量,用于判断是否已经被实例化
    private static Singleton uniqueSingleton;
    
    //私有化构造器,防止其他类进行实例化
    private Singleton() {
        
    }
    //返回所需的对象
    public static Singleton getInstance() {
        if(uniqueSingleton==null)
            uniqueSingleton=new Singleton();
        return uniqueSingleton;
    }
    
    public void showMessage() {
        System.out.println("hello world");
    }
}

 client.java客户端的使用

package Singleton;

public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Singleton single=Singleton.getInstance();
        single.showMessage();
    }

}

但是这样可能出现一个问题当我们另外开个线程的时候就会发现,这样的单例根本没有用。以下例子

package Singleton;

public class Singleton {
    
    //定义静态变量,用于判断是否已经被实例化
    private static Singleton uniqueSingleton;
    
    //私有化构造器,防止其他类进行实例化
    private Singleton() {
        
    }
    //返回所需的对象
    public static Singleton getInstance() {
        
            //需要消耗资源,和时间
            try {
                if(uniqueSingleton==null)
                {
                Thread.sleep(300);
                uniqueSingleton=new Singleton();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            
        return uniqueSingleton;
    }
    
    public void showMessage() {
        System.out.println("hello world");
    }
}

客户端采用多线程使用该单例:

package Singleton;

public class Client extends Thread {
    
    @Override
    public void run() {
        System.out.println(Singleton.getInstance().hashCode());
        
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        Client[] mts = new Client[10];
        for(int i = 0 ; i < mts.length ; i++){
            mts[i] = new Client();
        }
        
        for (int j = 0; j < mts.length; j++) {
            mts[j].start();
        }

        
        
    }

}

结果就会发现单例模式根本就没有用:

设计模式之--单例模式 随笔 第2张

可以从图中的哈希地址发现根本就不是同一个对象,这是因为:

线程1在判断完s==null后突然交换了cpu的使用权,变为线程2执行,由于s仍然为null,那么线程2中就会创建这个Singleton的单例对象。之后线程1拿回cpu的使用权,而正好线程1之前暂停的位置就是判断s是否为null之后,创建对象之前。这样线程1又会创建一个新的Singleton对象

以下又三种方法解决该单例问题

1.饿汉式

package Singleton;
//饿汉单例
public class Singleton {
    
    //因为在类加载时就已经实现实例化,所以不用担心线程问题
    private static Singleton uniqueSingleton=new Singleton(0;//但是在加载时可能会消耗更多时间
    
    //私有化构造器,防止其他类进行实例化
    private Singleton() {
        
    }
    //返回所需的对象
    public static Singleton getInstance() {
       
        uniqueSingleton=new Singleton();
        return uniqueSingleton;
    }
    
    public void showMessage() {
        System.out.println("hello world");
    }
}

2.线程同步

出现了上述问题时,我们就应该要考虑到同步(synchronized)问题了,当一个线程获得锁的时候其他线程就只能在外等着 ,这样就能解决线程不安全的问题了

package Singleton;

//懒汉式单例
public class Singleton {
  
  //定义静态变量,用于判断是否已经被实例化
  private static Singleton uniqueSingleton;
  
  //私有化构造器,防止其他类进行实例化
  private Singleton() {
      
  }
  //返回所需的对象
  public synchronized static Singleton getInstance() {
      if(uniqueSingleton==null)
          uniqueSingleton=new Singleton();
      return uniqueSingleton;
  }
  
  public void showMessage() {
      System.out.println("hello world");
  }
}

3.双重校验锁

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄