单例模式 &使用ThreadLocal实现

饿汉式单例(立即加载)

//饿汉式单例(立即加载)
public class SingletonEHan {
    
    
    //指向自己实例的私有静态引用,主动创建
    private static SingletonEHan singletonEHan = new SingletonEHan();

    //私有的构造方法
    public SingletonEHan() {
    
    
    }

    //以自己实例为返回值的静态的公有方法,静态工厂方法
    public static SingletonEHan getSingletonEHan() {
    
    
        return singletonEHan;
    }
}

懒汉式单例(延迟加载)

//懒汉式单例(延迟加载)
public class SingletonLHan {
    
    
    //指向自己实例的私有静态引用
    private static SingletonLHan singletonLHan;

    //私有构造方法
    private SingletonLHan() {
    
    
    }
    //使用synchronized修饰,临界资源的同步互斥访问。————同步延迟加载 线程安全
    //public static synchronized SingletonLHan getSingletonLHan() {
    
    

    //以自己实例为返回值的静态的公有方法,静态工厂方法
    public static SingletonLHan getSingletonLHan() {
    
    
        //synchronized (SingletonLHan.class){} ————同步延迟加载 线程安全
        if (singletonLHan == null) {
    
    
            //被动创建,在真正需要使用时才去创建
            singletonLHan = new SingletonLHan();
        }
        return singletonLHan;
    }
}

线程安全的懒汉式单例 双校验

//线程安全的懒汉式单例
public class SingletonDouble {
    
    
    //使用volatile关键字防止重排序,
    //因为new Instance()是一个非原子操作,创建一个不完整的实例
    private static volatile SingletonDouble singletonDouble;
    private SingletonDouble() {
    
    }

    public static SingletonDouble getSingletonDouble() {
    
    
        //第一次检测
        if (singletonDouble == null) {
    
    
            synchronized (SingletonDouble.class) {
    
    
                //只需在第一次创建实例时才同步
                if (singletonDouble == null) {
    
    
                    singletonDouble = new SingletonDouble();
                }
            }
        }
        return singletonDouble;
    }
}

ThreadLocal线程局部变量

public class SingletonTL {
    
    
    //ThreadLocal线程局部变量,将单例singletonTL线程私有化
    private static ThreadLocal<SingletonTL> singletonTLThreadLocal=new ThreadLocal<SingletonTL>();
    private static SingletonTL singletonTL;

    private SingletonTL(){
    
    }

    public static SingletonTL getSingletonTL() {
    
    
        //第一次检查,若线程第一次访问,则进入if语句块;
        //否则,若线程已经访问过,则直接返回ThreadLocal中的值
        if(singletonTLThreadLocal.get()==null){
    
    
            synchronized (SingletonTL.class){
    
    
                //第二次检查,该单例是否被创建
                if(singletonTL==null){
    
    
                    singletonTL=new SingletonTL();
                }
                singletonTLThreadLocal.set(singletonTL);
            }
        }
        return singletonTLThreadLocal.get();
    }
}

ThreadLocal

ThreadLocal是什么

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。所以ThreadLocal的应用场合,最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。

1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。

2、线程间数据隔离

3、进行事务操作,用于存储线程事务信息。

4、数据库连接,Session会话管理。

ThreadLocal怎么用

既然ThreadLocal的作用是每一个线程创建一个副本,我们使用一个例子来验证一下:
在这里插入图片描述

从结果我们可以看到,每一个线程都有各自的local值,我们设置了一个休眠时间,就是为了另外一个线程也能够及时的读取当前的local值。

那么为什么会在数据库连接的时候使用的比较多呢?
在这里插入图片描述

上面是一个数据库连接的管理类,我们使用数据库的时候首先就是建立数据库连接,然后用完了之后关闭就好了,这样做有一个很严重的问题,如果有1个客户端频繁的使用数据库,那么就需要建立多次链接和关闭,我们的服务器可能会吃不消,怎么办呢?如果有一万个客户端,那么服务器压力更大。

这时候最好ThreadLocal,因为ThreadLocal在每个线程中对连接会创建一个副本且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。是不是很好用。

ThreadLocal源码分析

在最开始的例子中,只给出了两个方法也就是get和set方法,其实还有几个需要我们注意。
在这里插入图片描述

方法这么多,我们主要来看set,然后就能认识到整体的ThreadLocal了:

1、set方法
在这里插入图片描述

从set方法我们可以看到,首先获取到了当前线程t,然后调用getMap获取ThreadLocalMap如果map存在,则将当前线程对象t作为key要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。

来看ThreadLocalMap。
在这里插入图片描述

我们可以看到ThreadLocalMap其实就是ThreadLocal的一个静态内部类,里面定义了一个Entry来保存数据,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。

总结一波

(1)每个Thread维护着一个ThreadLocalMap的引用

(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储

(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。

(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中

(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。

hreadLocalMap。**

(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中

(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。

(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

猜你喜欢

转载自blog.csdn.net/qq_43842093/article/details/129964770