单例模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的方式。
单例模式顾名思义就是只全局创建一个对象
若想达到此目的则需要满足
(1) 类提供对象不允许自行创建 这样就需要私有化构造器
(2) 类通过公有方法来暴露这个对象以便其他人拿到这个对象
类需要创建对象 但是类创建对象的时机可以不同以此作为分类 分成了饿汉式和懒汉式两种方式
(1)类在其他人获取对象之前就先创建好对象为饿汉式
(2)类在调用对象的时候才创建对象为懒汉式
PS : 两种方式创建对象都会产生一些问题
(1) 饿汉式 : 由于提前创建了对象就会占用内存 也不能实现懒加载
(2) 饿汉式 : 由于在调用的时候需要判断是否创建了对象 就会出现线程安全的问题
**解决方案 : **
(1) 对于饿汉式 需要解决占用内存和懒加载的问题 我们虽然不能保证类的加载是在调用的时候才会加载 但是可以保证方法的调用是即时的 那我们可以考虑在调用方法的时候加载一个类来保证调用时方法加载类 静态内部类刚好符合要求 可以将核心代码作为静态内部类嵌入一个类中 外部类调用方法时加载此类(代码详见目录2)
(2) 对于懒汉式 需要解决线程安全问题 解决线程安全问题首先想到加锁同步 但是这样效率会很低 为了提高效率 可以考虑缩小同步范围 (目录6) 但是这样单纯缩小范围if判断不能同步那么缩小范围就没有意义了 那么考虑在同步代码块中再次判断这样就起到了缩小同步范围的作用 但是这样在第一层判断进入的多个线程的属性仍然都为null 会重复new对象不能保证单例 考虑为对象添加volatile关键字来保证修改属性对其他线程可见 那么在这几个线程同步的时候if判断就可以保证单例
- 饿汉式(静态变量)
- 饿汉式(静态代码块)
- 静态内部类
- 懒汉式(线程不安全)
- 懒汉式(线程锁)
- 懒汉式(线程代码块)
- 懒汉式(双重检查)
- 枚举
package build.singleton;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Singleton {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//测试静态变量饿汉式 356573597 相同
print(SingletonEhan.class);
//测试懒汉式
print(SingletonLhan.class);
//测试懒汉式线程安全
print(SingletonLhanS.class);
//测试双向检查
print(SingletonLhanDB.class);
//测试静态内部类
print(SingletonC.class);
}
//反射调用instance方法 这里只测试了单例(线程安全没有测试)
public static void print(Class clazz) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method getInstance = clazz.getMethod("getInstance");
Object invoke = getInstance.invoke(clazz, (Object[]) null);
Object invoke2 = getInstance.invoke(clazz, (Object[]) null);
System.out.println(invoke.hashCode() == invoke2.hashCode());
}
}
class Print{
public static void print(Class clazz) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method getInstance = clazz.getMethod("getInstance");
Object invoke = getInstance.invoke(clazz, (Object[]) null);
Object invoke2 = getInstance.invoke(clazz, (Object[]) null);
System.out.println(invoke.hashCode() == invoke2.hashCode());
}
}
//静态变量饿汉式
//私有化构造器
//私有属性本类对象
//通过公有静态方法暴露对象
//优点 : 简单 不存在线程安全问题
//缺点 : 不能实现lazy load 占用内存
class SingletonEhan{
private final static SingletonEhan instance = new SingletonEhan();
private SingletonEhan(){}
public static SingletonEhan getInstance() {
return instance;
}
}
//静态代码块饿汉式
class SingletonEhanS{
private SingletonEhanS(){}
private static SingletonEhanS instance;
//使用静态代码块 创建单例对象
static{
instance = new SingletonEhanS();
}
public static SingletonEhanS getInstance(){
return instance;
}
}
//线程不安全懒汉式
//调用instance方法的时候才会new对象
//优点 : 可以实现懒加载 节省内存
//缺点 : 线程不安全
class SingletonLhan{
private static SingletonLhan instance;
private SingletonLhan(){}
public static SingletonLhan getInstance() {
if(instance == null){
instance = new SingletonLhan();
}
return instance;
}
}
//线程安全懒汉式
//使用同步解决线程安全问题
//但是这样效率会非常低 每次调用都需要同步 而只有在new的时候才需要同步
class SingletonLhanS{
private static SingletonLhanS instance;
private SingletonLhanS(){}
public synchronized static SingletonLhanS getInstance() {
if(instance == null){
instance = new SingletonLhanS();
}
return instance;
}
}
//线程安全懒汉式
//使用同步代码块
//但是这样效率虽然提高了 但是放在if里面 同步没有意义
//这种是达不到单例的
class SingletonLhanS2{
private static SingletonLhanS2 instance;
private SingletonLhanS2(){}
public static SingletonLhanS2 getInstance() {
if(instance == null){
synchronized(SingletonLhanS2.class){
instance = new SingletonLhanS2();
}
}
return instance;
}
}
//双重检查 懒汉式
//volatile 防止指令重排序 保证属性修改对其他线程可见
class SingletonLhanDB{
private static volatile SingletonLhanDB instance;
private SingletonLhanDB(){}
public static SingletonLhanDB getInstance() {
if(instance == null){
synchronized (SingletonLhanDB.class){
if(instance == null){
instance = new SingletonLhanDB();
}
}
}
return instance;
}
}
//静态内部类
//只有调用instance方法的时候类才会加载
//优点 : 既实现了懒加载又避免了线程安全问题
//缺点 : 多加载了一个类
class SingletonC{
private SingletonC(){}
private static class SingletonInstance{
private static final SingletonC instance = new SingletonC();
}
public static SingletonC getInstance(){
return SingletonInstance.instance;
}
}
//枚举单例模式 使用枚举可以实现单例
//可以避免多线程同步 还能方式反序列化
//作者推荐这种
enum SingletonE{
/*
* instance 实例
* */
INSTANCE;
}