单例模式

饿汉式

public class SingleTon1 implements Serializable {

    private static final long serialVersionUID = -3865630706729115005L;
    //new一个私有化对象
    private static  SingleTon1 singleTon1=new SingleTon1();
    //私有化构造
    private SingleTon1(){

    }
    //提供外界调用方法
    public static SingleTon1 getInstance(){
        return singleTon1;
    }

}

懒汉式-线程不安全

public class SingleTon2 {

    private volatile static SingleTon2 singleTon2 = null;

    private SingleTon2() {

    }

    public static SingleTon2 getInstance() {
        if (singleTon2 == null) {
            synchronized (SingleTon2.class) {
                if (singleTon2 == null) {
                    singleTon2 = new SingleTon2();
                }
            }
        }
        return singleTon2;
    }
}

Double check 机制,提高代码性能,避免每个线程都拿到锁

volatile 关键字

1、保证线程可见性

public class Demo3 {

    static class MyTest {

        volatile  public int number = 0;

        public void changeNumber(){
            number = 100;
        }
    }

    public static void main(String[] args) {
    	//主线程
        final MyTest myTest = new MyTest();
        
        //子线程
        new Thread(new Runnable() {
            public void run() {
                System.out.println(String.format("线程%s开始执行", Thread.currentThread().getName()));
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                myTest.changeNumber();
                System.out.println(String.format("线程%s的number:%d", Thread.currentThread().getName(), myTest.number));
            }
        }).start();

        while (myTest.number == 0){

        }
        System.out.println("执行完毕");
    }
}

属性不加volatile修饰,副本线程number改变,主线程不知道,while判断进入死循环

2、不保证原子性

singleTon2 = new SingleTon2();

反编译后可知:new命令分为三步: 1、分内存 2、初始化 3、存入

1、分配对象内存
2、调用构造器方法,执行初始化方法
3、将对象引用赋值给变量

多线程时存在线程安全问题

解决方法

方法一:使用 synchronized 关键字

//给函数增加synchronized修饰,相当于加锁了
 public synchronized void incr(){
     number++;
 }

方法二:使用AtomicInteger

public class Demo8 {

    static class MyTest {

        public volatile AtomicInteger number = new AtomicInteger();
        
        public void incr(){
            number.getAndIncrement();
        }
    }


    public static void main(String[] args) {
        MyTest myTest = new MyTest();

        for (int i = 1; i <= 10; i++){

            new Thread(() -> {
                for (int j = 1; j <= 1000; j++){
                    myTest.incr();
                }
            }, "Thread"+String.valueOf(i)).start();
        }

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //等线程执行结束了,输出number值
        System.out.println("当前number:" + myTest.number);
    }
}

3、禁止指令重排

指令重排不影响最后结果但是会自行优化,导致顺序变动

指令重排不影响单线程结果,但在多线程中存在问题

例如:

双重检查锁的代码中,如果线程一获取锁进入到创建实例,此时发生指令重排,线程一执行到t3,线程二刚好进入,由于此时对象已经不为空,所以线程二可以自由访问对象,然该对象还未初始化,所以线程二访问就出现了问题。

静态内部类

public class SingleTon3 implements Serializable {

    private static final long serialVersionUID = -7317901076499532518L;

    /**
     *  静态内部类
     */
    public static class MySingletonHandler {
        private static SingleTon3 instance = new SingleTon3();
    }

    private SingleTon3() {

    }

    public static SingleTon3 getInstance() {
        return MySingletonHandler.instance;
    }
}

静态代码块

public class SingleTon4 {

    private static SingleTon4 instance=null;

    private SingleTon4(){

    }

    /**
     * 静态代码块
     */
    static {
        instance=new SingleTon4();
    }

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

枚举

public enum Singleton5 implements Serializable {

    INSTANCE; //对象

    public void doSomething() {
        System.out.println("doSomething");
    }

}

细节:前四种单例模式,在序列化与反序列化中存在问题。对象不唯一

只有枚举的单例模式,不存在任何问题

posted @   东楚  阅读(65)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示