Java------多线程_高级主题_dcl单例模式_ThreadLocal(十五)
对外只有一个对象,使用一个地址。
单例模式模式有多种书写模式,此处volatile和double-checking实现。
package cooperation;
/**
* 单例模式:对外存在一个对象(懒汉式基础上加上并发控制)
* 1.构造器私有化,避免new对象
* 2.提供私有静态属性,存储对象的地址
* 3.提供公共静态方法,访问该静态属性
*/
public class DoubleChecking {
//2.私有化静态属性,volatile加到这里避免指令重排
private static volatile DoubleChecking doubleChecking;
//1.构造器私有化
private DoubleChecking(){
}
//3.提供公共静态方法,访问该静态属性
public static DoubleChecking getDoubleChecking(){
//再次检测
if (null != doubleChecking){
//避免不必要的同步,已经存在对象
return doubleChecking;
}
//可以加synchronized,但是粒度比较大
synchronized (DoubleChecking.class){
if (null == doubleChecking){
doubleChecking = new DoubleChecking();
//1.开辟空间 2.初始化对象信息 3.返回对象的地址引用
//new这一步可能会存在指令重排,A线程执行到第二步,B线程认为已经存在对象,结果拿到null
//没有线程可能访问到没有初始化的对象
}
}
return doubleChecking;
}
public static void main(String[] args) {
new Thread(() ->{
System.out.println(DoubleChecking.getDoubleChecking());
}).start();
System.out.println(DoubleChecking.getDoubleChecking());
}
}
ThreadLocal
1.在多线程环境下,每个线程都有自己的数据,一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看到,不会影响其他线程。
2.ThreadLocal能够放一个线程级别的变量,其本身能够被多线程共享使用,并且又能够达到线程安全的目的,在多线程的环境下保证成员变量的安全,常用方法:get/set/initalValue方法。
3.JDK建议ThreadLocal定义为private static
4.ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的方法都可以非常方便的访问这些资源
比如:Hibernaete的Session工具类HibernateUti
比如:通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。
案例:
package cooperation;
/**
* ThreadLocal:每个线程自身的存储本地,局部区域
*
*/
public class ThreadLocalTest01 {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
//更改初始值,重写initalValue
private static ThreadLocal<Integer> threadLocal02 = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 200;
}
};
//第二种重写方法 lambda表达式,只有一句话可以省略
private static ThreadLocal<Integer> threadLocal03 = ThreadLocal.withInitial(() ->200);
public static void main(String[] args) {
//获取值-----初始值为null\200
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal02.get());
//设置值
threadLocal02.set(66);
//打印结果不一样
new Thread(new MyRun()).start();
new Thread(new MyRun()).start();
}
static class MyRun implements Runnable{
@Override
public void run() {
threadLocal02.set(99);
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal02.get());
}
}
}
ThreadLocal每一个线程有自己的存储区域,更改不会影响其他的线程
案例:
/**
* ThreadLocal每一个线程有自己的存储区域,更改不会影响其他的线程
*/
public class ThreadLocalTest02 {
private static ThreadLocal<Integer> threadLocal03 = ThreadLocal.withInitial(() ->1);
public static void main(String[] args) {
for (int i= 0;i<5;i++){
new Thread(new MyRun()).start();
}
}
static class MyRun implements Runnable{
@Override
public void run() {
Integer integer = threadLocal03.get();
System.out.println(Thread.currentThread().getName()+"--->得到了:"+integer);
threadLocal03.set(integer-1);
System.out.println(Thread.currentThread().getName()+"--->还剩下:"+threadLocal03.get());
// Thread-0--->得到了:1
// Thread-0--->还剩下:0
// Thread-1--->得到了:1
// Thread-1--->还剩下:0
// Thread-2--->得到了:1
// Thread-2--->还剩下:0
// Thread-3--->得到了:1
// Thread-3--->还剩下:0
// Thread-4--->得到了:1
// Thread-4--->还剩下:0
}
}
}
案例三:main线程和主线程
/**
* ThreadLocal:分析上下文环境
* 1.构造器:那里调用就属于哪里
* 2.run方法就是本线程自身的
*/
public class ThreadLocalTest03 {
private static ThreadLocal<Integer> threadLocal03 = ThreadLocal.withInitial(() ->1);
public static void main(String[] args) {
new Thread(new MyRun()).start();
}
static class MyRun implements Runnable{
public MyRun(){
//此处是main线程,打印出main--->1
Integer integer = threadLocal03.get();
System.out.println(Thread.currentThread().getName()+"--->:"+integer);
}
@Override
public void run() {
Integer integer = threadLocal03.get();
//此处是thread0线程,打印出thread0-->1
System.out.println(Thread.currentThread().getName()+"--->:"+integer);
}
}
}
InheritableThreadLocal:继承上下文环境的数据,拷贝父线程的数据
案例四:
/**
* InheritableThreadLocal:继承上下文环境的数据
*/
public class ThreadLocalTest04 {
private static ThreadLocal<Integer> threadLocal03 = new InheritableThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"--->:"+threadLocal03.get());
threadLocal03.set(2);
//线程由main线程开辟,因此会继承main,copy了一份,
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--->:" + threadLocal03.get());
threadLocal03.set(5);
});
thread.start();
thread.join();
System.out.println(Thread.currentThread().getName()+"--->:"+threadLocal03.get());
// main--->:null
// Thread-0--->:2
// main--->:2
}
}