java实现单例模式

懒汉式线程不安全
当第一次调用getInstance()方法时,如果多个线程几乎同时进入if (instance == null)的判断语句,并且此时instance确实为null,这些线程都有机会执行instance = new Test()来创建单例对象。由于构造函数没有被同步,可能会有多个线程分别执行该构造函数,从而导致多个不同的单例实例被创建
代码

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

public class scratch_7 {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    Test test = Test.getInstance();
                    System.out.println(test);
                }
            });
        }
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(2, TimeUnit.MINUTES)) {
                executorService.shutdownNow();
                System.out.println("强制关闭");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束");
    }

}

class Test {

    private static Test instance;

    private Test() {}

    public static Test getInstance(){
        if (instance == null) {
            instance = new Test();
        }
        return instance;
    }
}

class Test2 {

}

输出结果

Test@2b47f4bb
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@4e3058f8
Test@5bd863fa

懒汉式线程安全
通过加锁机制(如synchronized关键字)确保线程安全,但这可能会降低性能,因为每次调用getInstance()时都会进行同步检查

class Test {

    private static Test instance;

    private Test() {
    }

    public synchronized static Test getInstance() {
        if (instance == null) {
            instance = new Test();
        }
        return instance;
    }
}

双重检查锁定
这种改进的懒汉式实现在第一次检查实例是否为null时不加锁,只在实例真正创建时同步,这提高了性能。

class Test {

    private static Test instance;

    private Test() {
    }

    public static Test getInstance() {
        if (instance == null) {
        	// 不能是instance,因为第一次进来instance是null,会抛出异常
            synchronized (Test.class) {
                if (instance == null) {
                    instance = new Test();
                }
            }
        }
        return instance;
    }
}

饿汉式
在这种模式下,单例实例在类加载时就创建,由于ClassLoader 方法在加载类时使用了 synchronized 关键字,确保了同一时间只有一个线程可以执行类的加载过程,从而避免了多线程环境下可能出现的并发问题,但如果没有使用这个实例,也会造成内存浪费

class Test {

    private static final Test instance = new Test();

    private Test() {
    }

    public static Test getInstance() {
        return instance;
    }
}

静态内部类
这种方式同样利用了 ClassLoader 机制来保证初始化 instance 时只有一个线程,它跟上一种方式不同的是:只要 Test 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 TestHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 TestHolder 类

class Test {

    private static class TestHolder{
        private static Test instance = new Test();
    }

    private Test() {
    }

    public static Test getInstance() {
        return TestHolder.instance;
    }
}

枚举
枚举是实现单例的一种简洁、线程安全且无需考虑反射和序列化问题的方式。枚举天然具备单例的特性,由JVM保证其唯一性,且默认就是线程安全的

public enum Test {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

总结
一般情况下,不建议使用懒汉式线程不安全懒汉式线程安全,建议使用饿汉式。只有在要明确实现 lazy loading 效果时,才会使用双重检查锁定。如果涉及到反序列化创建对象时,可以尝试使用枚举。如果有其他特殊的需求,可以考虑使用双重检查锁定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值