单例模式

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

业务中只需要一个实例对象,考虑用单例模式

 https://www.jb51.net/article/213888.htm#_label2   

饿汉式  

缺点: 类加载就初始化,浪费内存
优点: 没有加锁,执行效率高。还是线程安全的实例。

因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用。单例就是该类只能返回一个实例。

换句话说,在线程访问单例对象之前就已经创建好了。再加上,由于一个类在整个生命周期中只会被加载一次,因此该单例类只会创建一个实例。

也就是说,线程每次都只能也必定只可以拿到这个唯一的对象。即饿汉式单例天生就是线程安全的。

public class Ehan{
    private Ehan(){}  //私有构造器 保证内存中只有一个对象
    private static final Ehan ehan  =new Ehan();
    public  static Ehan getInstance(){
        return ehan;
    }

}

懒汉式 

用的时候再去加载,相对 节约了内存

存在线程安全问题

public class Lhan {
    private Lhan() {
        
    }

    private static Lhan lhan;

    public static Lhan getInstance() {
        if (lhan == null) {
          
            lhan = new Lhan();
        }

        return lhan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lhan.getInstance();
            }).start();
        }
    }
}

实例在调用 getInstance 才会创建实例,这样的优点是不占内存,在单线程模式下,是安全的。但是多线程模式下,多个线程同时执行 if (ehSingleton == null) 结果都为 true会创建多个实例,所以上面的懒汉单例是一个线程不安全的实例。

 

双检锁

public class Lhan {
    private Lhan() {
        System.out.println(Thread.currentThread().getName() + " " + "ok");
    }

    private static Lhan lhan;

    public static Lhan getInstance() {
        if(lhan==null){  //第一层是为了提高效率,减少线程对同步锁的竞争 。只有if ==true 才会获取锁
            synchronized (Lhan.class) {
                if (lhan == null) {  //第二层是为了单例
                    lhan = new Lhan();
                }
            }
        }
        return lhan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lhan.getInstance();
            }).start();
        }
    }
}

Thread-1 ok   只执行一次 只有一个实例

    public static Lhan getInstance() {
        if (lhan == null) {
            synchronized (Lhan.class) {
                if (lhan == null) {
                    lhan = new Lhan();//不是原子性操作
1 分配空间
2 执行构造方法 初始化对象
3 对象指向空间
线程A 132
第3步时线程B来了,B认为lhan!=null ,就会直接返回,但是此时
lhan没有初始化
所以可能出现指令重排优化 需要用volatile
                }
            }

        }

        return lhan;
    }

 

因此

这种最好

public class Lhan {
    private Lhan() {
        System.out.println(Thread.currentThread().getName() + " " + "ok");
    }

    private static volatile Lhan lhan;  //volatile  为了防止指令重排优化

    public static Lhan getInstance() {
        if(lhan==null) {
            synchronized (Lhan.class) {
                if (lhan == null) {
                    lhan = new Lhan();
                }
            }
}
        

        return lhan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lhan.getInstance();
            }).start();
        }
    }
}

内存占用率高,效率高,线程安全

但是依然可以用反射来破坏单例模式

静态内部类

静态内部类为什么是线程安全
静态内部类利用了类加载机制的初始化阶段 方法,静态内部类的静态变量赋值操作,实际就是一个 方法,当执行 getInstance() 方法时,虚拟机才会加载 SingletonHolder 静态内部类,

然后在加载静态内部类,该内部类有静态变量,JVM会改内部生成方法,然后在初始化执行方法 —— 即执行静态变量的赋值动作。

虚拟机会保证 方法在多线程环境下使用加锁同步,只会执行一次 方法。

这种方式不仅实现延迟加载,也保障线程安全。

public class Holder {
    private Holder() {
        System.out.println(Thread.currentThread().getName() + " " + "ok");
    }

    public static Holder getInstance() {
        return Innerclass.holder;
    }

    public static class Innerclass {
        private static final Holder holder = new Holder();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Thread(() -> {
                Holder.getInstance();
            }).start();
        }
    }
}

枚举

enum 本身也是一个class 类

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class Test {
    public static void main(String[] args) {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

 

INSTANCE
INSTANCE

posted on 2022-03-26 21:50  cltt  阅读(37)  评论(0编辑  收藏  举报

导航