ThreadLocal

ThreadLocal:线程级别的私有变量

ThreadLocal使用:
1.set(T):将变量存放在线程中。
2.get(T):从线程中取得私有变量
3.remove():从线程中移除私有变量。
4.initialValue:初始化
5.withInitial:初始化

正常存取操作
initialValue+get

/**
 * ThreadLocal正常存取操作
 */
package threadPool0523;

public class   ThreadPoolDemo13 {
    //创建并初始化
    static ThreadLocal<String> threadLocal=
            new ThreadLocal(){
                @Override
                protected Object initialValue() {
                    System.out.println("执行了初始化方法");
                    return "Java";
                }
            };

    public static void main(String[] args) {
        String result=threadLocal.get();
        System.out.println("从ThreadLocal中得到 "+result);
    }

}

initialValue + set + get会怎么执行

/**
 * initialValue + set + get
 * 怎么执行
 */
package threadPool0523;

public class ThreadPoolDemo14 {
    static ThreadLocal<String> threadLocal=new ThreadLocal(){
        @Override
        protected Object initialValue() {
            System.out.println("执行了初始化方法");
            return "Java";
        }
    };

    public static void main(String[] args) {
        threadLocal.set("Mysql");
        String result=threadLocal.get();
        System.out.println("执行结果 "+result);
    }
}

在这里插入图片描述

面试问题:
什么情况下不会执行initialValue? 为什么不会执行?
答:set之后就不会执行。ThreadLocal是懒加载的,当调用了get方法之后,才会尝试执行initialValue方法,尝试获取一下ThreadLocal set的值,如果获取到值,那么初始化方法不会被执行

withInitial方法(静态方法)

/**
 *
 * withinitial方法
 */
package threadPool0523;

import java.util.function.Supplier;

public class ThreadPoolDemo15 {
    static ThreadLocal<String> threadLocal=
            ThreadLocal.withInitial(new Supplier<String>() {
                @Override
                public String get() {
                    System.out.println("执行了初始化方法");
                    return "Java";
                }
            });

    public static void main(String[] args) {
        String result=threadLocal.get();
        System.out.println("结果:"+result);
    }
}

lamda表达式

public class ThreadPoolDemo16 {
    static ThreadLocal<String> threadLocal=
            ThreadLocal.withInitial(()->"Java");

    public static void main(String[] args) {
        String result=threadLocal.get();
        System.out.println("结果:"+result);
    }
}

ThreadLocal实现1000个时间格式化,无线程不安全问题

/**
 * 实现1000个时间格式化
 */
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo17 {
    static ThreadLocal<SimpleDateFormat> threadLocal=
            ThreadLocal.withInitial(()->
                    new SimpleDateFormat("mm:ss"));

    public static void main(String[] args) {
        ThreadPoolExecutor executor=new ThreadPoolExecutor(10,10,0,
                TimeUnit.SECONDS,new LinkedBlockingDeque<>(1000));
        for (int i = 1; i <1001 ; i++) {
            int finalI=i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    Date date=new Date(finalI*1000);
                    myFormat(date);
                }
            });
        }

    }

    private static void myFormat(Date date) {
        String result=threadLocal.get().format(date);
        System.out.println("时间:"+result);

    }

}

ThreadLocal使用场景:
1.解决线程安全问题
2.实现线程级别的数据传递

ThreadLocal 缺点:
1.不可继承(子线程中不能读取到父线程的值)

2.脏读(脏数据)
在一个线程中,读取到了不属于自己的数据

/**
 * 使用线程池会出现  脏读
 */
package threadPool0523;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThradPoolDemo18 {
    static ThreadLocal<String> threadLocal=
            new ThreadLocal<>();

    public static void main(String[] args) {
        ThreadPoolExecutor executor=
                new ThreadPoolExecutor(1,1,0,
                        TimeUnit.SECONDS,new LinkedBlockingDeque<>(1000));
        for (int i = 0; i <2 ; i++) {
            MyTask t=new MyTask();
            executor.execute(t);
        }


    }

    static class MyTask extends Thread{
        //标识是否第一次访问
        static boolean first = true;


        @Override
        public void run() {
            if(first){
                //第一次访问,存储用户信息
                threadLocal.set(this.getName()+"  session info.");
                first=false;

            }
            String result=threadLocal.get();
            System.out.println(this.getName()+": "+result);
        }
    }

}

在这里插入图片描述

线程使用ThreadLocal不会出现脏读->每个线程都使用自己的变量值和ThreadLocal
线程池里面使用ThreadLocal就会出现脏读数据,线程池会复用线程,复用线程之后,也会复用线程中的静态属性,从而导致某些方法不能被执行,于是就出现了脏数据的问题

脏数据的解决方案:
1.避免使用静态变量(静态属性会在线程池中复用)
2.使用remove解决

缺点3:内存溢出问题(最常出现问题)、打开数据连接但未关闭

内存溢出:当一个线程执行完之后,不会释放这个线程所占用内存,或者释放内存不及时的情况都叫做内存溢出。->线程不用了,但线程相关的内存还得不到及时的释放

内存溢出:ThreadLocal + 线程池(线程池是长生命周期)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值