ThreadLocal

ThreadLocal是什么

ThreadLocal提供线程局部变量。这些变量与普通的变量不同之处在于,每个访问这种变量的线程(通过它的get或set方法)都有自己的、独立初始化的变量副本

ThreadLocal实例通常是希望将状态关联到一个线程的类的私有静态字段(比如,user ID 或者 Transaction ID 等等)

画外音:这段话表达了三个意思

ThreadLocal是一种变量类型,我们称之为“线程局部变量”

每个线程访问这种变量的时候都会创建该变量的副本,这个变量副本为线程私有
 

  • 每个ThreadLocal只能保存一个变量副本,如果想要上线一个线程能够保存多个副本以上,就需要创建多个ThreadLocal。
  • ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的风险。
  • 适用于无状态,副本变量独立后不影响业务逻辑的高并发场景。如果如果业务逻辑强依赖于副本变量,则不适合用ThreadLocal解决,需要另寻解决方案
  • 应用场景: JDBC ,session链接等

     比如最简单的JDBC,链接模拟如下,这种在多线程情况下,可能就会出现问题:

package book.thread.pool;

import static book.thread.pool.ThreadLocalCase.selectSql;

public class ThreadLocalCase {

    public static Boolean isOpen = false;

    /**
     * 打开连接
     */
    public static void openConnection() {

        if (isOpen) {
            System.out.println(Thread.currentThread().getName() + "打开链接,失败  isOpen= " + isOpen);
            return;
        }
        isOpen = true;
        System.out.println(Thread.currentThread().getName() + "打开链接成功  isOpen= " + isOpen);
    }


    /**
     * 断开连接
     */
    public static void closeConnection() {

        if (isOpen) {
            isOpen = false;
            System.out.println(Thread.currentThread().getName() + "断开链接成功  isOpen=" + isOpen);
        }
    }

    /**
     * 执行sql
     */
    public static void executeSql() throws InterruptedException {
        if (isOpen) {
            System.out.println(Thread.currentThread().getName() + "Sql 正在执行中......");
            Thread.sleep(2000);
        }
    }

    /**
     * 执行
     */
    public static void selectSql() throws InterruptedException {

        openConnection();

        executeSql();

        closeConnection();
    }

    public static void main(String args[]) {

//        selectSql();

        for (int i = 0; i < 4; i++) {
            ThreadLocalCaseJdBC stringThread = new ThreadLocalCaseJdBC();
            Thread thread = new Thread(stringThread);
            thread.start();
        }
    }
}

class ThreadLocalCaseJdBC implements Runnable {

    @Override
    public void run() {
        // System.out.println("当前线程--->" + Thread.currentThread().getName());
        try {
            selectSql();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 结果如下:

     

    这种情况下就必须要用到ThreadLocal了

    代码修改为:

package book.thread.pool;

import static book.thread.pool.ThreadLocalCase.selectSql;

public class ThreadLocalCase {


    private static ThreadLocal<Boolean> threadId = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return false;
        }
    };

    /**
     * 打开连接
     */
    public static void openConnection() {

        if (threadId.get()) {
            System.err.println(Thread.currentThread().getName() + "打开链接,失败  isOpen= " + threadId.get());
            return;
        }

        threadId.set(true);
        System.out.println(Thread.currentThread().getName() + "打开链接成功  isOpen= " + threadId.get());
    }


    /**
     * 断开连接
     */
    public static void closeConnection() {

        if (threadId.get()) {
            threadId.set(false);
            System.err.println(Thread.currentThread().getName() + "断开链接成功  isOpen=" + threadId.get());
        }
    }

    /**
     * 执行sql
     */
    public static void executeSql() throws InterruptedException {
        if (threadId.get()) {
            System.out.println(Thread.currentThread().getName() + "Sql 正在执行中......");
            Thread.sleep(2000);
        }
    }

    /**
     * 执行
     */
    public static void selectSql() throws InterruptedException {

        openConnection();

        executeSql();

        closeConnection();
    }

    public static void main(String args[]) {

        for (int i = 0; i < 100; i++) {
            ThreadLocalCaseJdBC stringThread = new ThreadLocalCaseJdBC();
            Thread thread = new Thread(stringThread);
            thread.start();
        }
    }
}

class ThreadLocalCaseJdBC implements Runnable {

    @Override
    public void run() {
        // System.out.println("当前线程--->" + Thread.currentThread().getName());
        try {
            selectSql();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadLocal每个线程单独赋值

package book.thread.pool;

import java.util.HashMap;
import java.util.Map;

import static book.thread.pool.ThreadId.printObject;

public class ThreadId {

    public static final  int MAP_MAX_SIZE = 10;
    public static final  int MAX_INITIALIZE_THREAD_COUNT  = 100;

    private static ThreadLocal<Map<String, String>> threadId = new ThreadLocal<Map<String, String>>() {
        @Override
        protected Map<String, String> initialValue() {
            Map<String, String> map = new HashMap<>();
            map.put(Thread.currentThread().getName(), Thread.currentThread().getName());
            return map;
        }
    };


    public static Map<String, String> get() {
        return threadId.get();
    }

    public static Map<String, String> putMap() {

        Map<String, String> map = threadId.get();
        for (int i = 0; i < MAP_MAX_SIZE; i++) {
            map.put(Thread.currentThread().getName() + ":" + i, String.valueOf(i));
        }
        return map;
    }

    public static Map<String, String> reMoveMap() {

        Map<String, String> map = threadId.get();
        for (int i = 0; i < MAP_MAX_SIZE; i++) {
            map.remove(Thread.currentThread().getName() + ":" + i);
        }
        return map;
    }

    public static void printObject(String format,Object ...args) {
       System.out.println(String.format(format, args));
    }


    public static void main(String args[]) {

        for (int i = 0; i < MAX_INITIALIZE_THREAD_COUNT; i++) {
            ThreadLocalMap threadLocalMap = new ThreadLocalMap();
            Thread thread = new Thread(threadLocalMap);
            thread.start();
        }
    }
}

class ThreadLocalMap implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("线程--->" + Thread.currentThread().getName() +"");
            Thread.sleep(1000);

            printObject("线程%s初始Map值为:%s",Thread.currentThread().getName(),ThreadId.get());
            printObject("线程%s putMap值后为:%s",Thread.currentThread().getName(),ThreadId.putMap());
            printObject("线程%s removeMap值后为:%s",Thread.currentThread().getName(),ThreadId.reMoveMap());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值