目录

1.概念

2.饿汉模式

2.1概念

2.2代码实例

3.懒汉模式

3.1概念

 3.2代码示例

3.2.1解决懒汉线程安全问题——加锁

 3.2.2 解决懒汉线程安全问题——优化加锁方法


1.概念

在软件开发过程中有很多常见的问题场景,设计模式就是为了解决这些这些问题而形成的特定的解决思路。用固定的思路来实现代码,就可以轻松地解决问题。

单例模式就是常用的设计模式之一。

单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例。(在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,创建一个对象,其他调用时共享着一个对象。)

单例模式的具体实现方式有两种:“饿汉”和“懒汉”

2.饿汉模式

2.1概念

在类加载的同时就已经创建好了该单例对象,等待被程序使用。在程序调用时直接返回该单例对象。

android 单例线程管理 线程单例模式_单例模式

2.2代码实例

public class Singleton {
    //类成员变量
    private static Singleton instance=new Singleton();

    //重写无参构造方法
    private Singleton(){

    }
    //对外提供一个类成员变量的方法
    public static Singleton getInstance(){
        return instance;
    }

}

 因为饿汉模式在类创建时就创建了一个单例模式供系统使用,且不会被改变。所以饿汉模式天生就是线程安全的。

3.懒汉模式

3.1概念

懒汉模式创建对象时,先判断该对象是否有被实例化。如果已经被实例化就直接返回该实例对象,如果没有先进行实例化操作。

android 单例线程管理 线程单例模式_java-ee_02

 3.2代码示例

public class SingletonLazy {
    //定义一个类的成员变量
    private static SingletonLazy instance=null;
    //私有化构造方法
    private SingletonLazy(){

    }
    //对外提供一个获取单例对象的方法
    public static SingletonLazy getInstance(){
        //判断需要返回的对象是否为空
        if (instance==null){
            //创建
            instance=new SingletonLazy();
        }
        //返回
        return instance;
    }

}

SingletonLazy通过将构造方法用private修饰能够防止该类在外部被实例化。在同一个虚拟机范围内,SingletonLazy的唯一实例只能通过getInstance()方法访问。

但是,这种方法是线程不安全的。当两个线程同时判断该实例对象为空,就会创建两个实例化对象。

//演示懒汉式单例模式的线程不安全情况
public class Demo_Singleton03 {
    public static void main(String[] args) {
        //多个线程并发获取单例模式
        for (int i=0;i<10;i++){
            //10个线程
            Thread thread=new Thread(()->{
                //获取单例
               SingletonLazy instance=SingletonLazy.getInstance();
                System.out.println(instance);
            });
            thread.start();
        }
    }
}

3.2.1解决懒汉线程安全问题——加锁

可以给方法加锁,也可以给类对象加锁。

//给方法加锁
public static synchronized Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}

加锁避免了创建多个实例化对象的问题,但是还会有另一个问题:每次去创建对象时都要去获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。

 3.2.2 解决懒汉线程安全问题——优化加锁方法

为了优化加锁方法,我们需要实现的是:当没有实例化对象时获取锁创建实例化对象;如果有实例化对象时,不获取锁直接返回实例化对象。

class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
           if (instance == null) {
               instance = new Singleton();
               }
           }
       }
        return instance;
   }
}

 使用双重 if 判定, 降低锁竞争的频率.;给 instance 加上了 volatile.

外层的if是为了判断实例是否被创建。volatile避免了“内存可见性”问题。

1.当三个线程同时执行getInstance方法时,当经过第一层if时都收到了实例没有被创建的消息,于是开始竞争锁。

2.假如线程一获取到锁到达第二层if时,发现实例也没有被创建于是创建了实例,最后释放了锁。

3.当线程一释放了锁,线程二和线程三无论哪一方竞争到了锁到达第一个if时,发现实例已经被创建就不会再去获取锁了。

DCL——Double Check Lock 可以实现在第一次创建时实现同步。所谓的双重校验锁,就是在锁的内部和外部分别对instance进行为null的校验。