单例模式

一个栗子:

public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() { }
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1.equals(singleton2));
    }
}

注意构造器是private的,然后uniqueInstance是静态的。静态变量记录该类的唯一实例。

package src.Singleton;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 单例模式确一个类只有一个实例,并提供一个全局访问点
 * 单例模式可以延迟初始化,在药品使用这个对象的时候才去创建它
 */

public class ChocolateBoiler {
    private boolean empty;
    private boolean boiled;

    private static ChocolateBoiler uniqueInstance;

    private ChocolateBoiler() {
        empty = true;
        boiled = false;
    }

    public static ChocolateBoiler getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new ChocolateBoiler();
        }
        return uniqueInstance;
    }

    private boolean isEmpty() {return empty;}
    private boolean isBoiled() {return boiled;}

    public void fill() {
        if (!isEmpty()) {
            empty = false;
            boiled = false;
        }
    }

    public void drain() {
        if (!isEmpty() && isBoiled()) {
            empty = true;
        }
    }

    public void boil() {
        if (!isEmpty() && !isBoiled()) {
            boiled = true;
        }
    }

    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        Thread thread = new Thread(){
            public void run() {
                ChocolateBoiler chocolateBoiler = ChocolateBoiler.getInstance();
                chocolateBoiler.fill();
                chocolateBoiler.boil();
                chocolateBoiler.drain();
            }
        };
        for (int i = 0; i < 5; i++) {
            service.execute(thread);  //2个线程同时执行
        }
        service.shutdown();
    }
}

想一想,这会存在什么问题吗?

是不是任何情况下都只是唯一的实例?当我们遇到多线程的时候,事情可能会变得不一样。

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

    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1.equals(singleton2));
    }
}

解决方法很简单,就是在getinstance方法前加上

synchronized

但是这样直接对方法加锁的性能很低。

一些改进的方案:

1)对性能要求不高,就私用原来的直接同步方法

2)不延迟初始化,而是在jvm加载类的时候就创建实例 => 急切初始化

如果程序一定会创建实例

public class ThreadSafeSingleton {
    private static ThreadSafeSingleton uniqueInstance = new ThreadSafeSingleton();
    
    private ThreadSafeSingleton() {}

    public static ThreadSafeSingleton getInstance() {
        return uniqueInstance;
    }
}

3)双重加锁

 性能大大提升

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

这三种就是线程安全的单例模式的三种形式,各自有各自的适用情况。

原文地址:https://www.cnblogs.com/shawshawwan/p/9185262.html