饿汉式单例(立即加载)
//饿汉式单例(立即加载)
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。