Java------多线程_高级主题_dcl单例模式_ThreadLocal(十五)

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
    }
}

猜你喜欢

转载自blog.csdn.net/cz_chen_zhuo/article/details/121833894