【Java 并发】详解 ThreadLocal

本文详细介绍了 Java 中的 ThreadLocal,主要用于提供线程局部变量,避免并发问题。线程局部变量使得每个线程拥有独立的副本,避免了共享导致的并发冲突。文章通过示例解释了 ThreadLocal 的工作原理,包括如何存储线程副本变量、存取副本变量以及其散列值的计算,同时讨论了 ThreadLocalMap 的实现细节,如开放地址法处理冲突和弱引用防止内存泄漏。此外,还探讨了 ThreadLocal 在数据库连接和 Session 管理等场景的应用。
摘要由CSDN通过智能技术生成

前言

ThreadLocal 主要用来提供线程局部变量,也就是变量只对当前线程可见,本文主要记录一下对于 ThreadLocal 的理解。更多关于 Java 多线程的文章可以转到 这里。

线程局部变量

在多线程环境下,之所以会有并发问题,就是因为不同的线程会同时访问同一个共享变量,例如下面的形式

public class MultiThreadDemo {
   

    public static class Number {
   
        private int value = 0;

        public void increase() throws InterruptedException {
   
            value = 10;
            Thread.sleep(10);
            System.out.println("increase value: " + value);
        }

        public void decrease() throws InterruptedException {
   
            value = -10;
            Thread.sleep(10);
            System.out.println("decrease value: " + value);
        }
    }//需要获取资料的朋友请加Q君样:290194256*


    public static void main(String[] args) throws InterruptedException {
   
        final Number number = new Number();
        Thread increaseThread = new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                try {
   
                    number.increase();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        });

        Thread decreaseThread = new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                try {
   
                    number.decrease();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        });

        increaseThread.start();
        decreaseThread.start();
    }
}

在上面的代码中,increase 线程和 decrease 线程会操作同一个 number 中 value,那么输出的结果是不可预测的,因为当前线程修改变量之后但是还没输出的时候,变量有可能被另外一个线程修改,下面是一种可能的情况:

increase value: 10
decrease value: 10

一种解决方法是在 increase() 和 decrease() 方法上加上 synchronized 关键字进行同步,这种做法其实是将 value 的 赋值 和 打印 包装成了一个原子操作,也就是说两者要么同时进行,要不都不进行,中间不会有额外的操作。我们换个角度考虑问题,如果 value 只属于 increase 线程或者 decrease 线程,而不是被两个线程共享,那么也不会出现竞争问题。一种比较常见的形式就是局部(local)变量(这里排除局部变量引用指向共享对象的情况),如下所示:

public void increase() throws InterruptedException {
   
    int value = 10;
    Thread.sleep(10);
    System.out.println("increase value: " + value);
}

不论 value 值如何改变,都不会影响到其他线程,因为在每次调用 increase 方法时,都会创建一个 value 变量,该变量只对当前调用 increase 方法的线程可见。借助于这种思想,我们可以对每个线程创建一个共享变量的副本,该副本只对当前线程可见(可以认为是线程私有的变量),那么修改该副本变量时就不会影响到其他的线程。一个简单的思路是使用 Map 存储每个变量的副本,将当前线程的 id 作为 key,副本变量作为 value 值,下面是一个实现:

public class SimpleImpl {
   

    public static class CustomThreadLocal {
   
        private Map<Long, Integer> cacheMap = new HashMap<>();

        private int defaultValue ;

        public CustomThreadLocal(int value) {
   
            defaultValue = value;
        }

        public Integer get() {
   
            long id = Thread.currentThread().getId();
            if (cacheMap.containsKey(id)) {
   
                return cacheMap.get(id);
            }
            return defaultValue;
        }

        public void set(int value) {
   
            long id = Thread.currentThread().getId();
            cacheMap.put(id, value);
        }
    }

    public static class Number {
   
        private CustomThreadLocal value = new CustomThreadLocal(0);

        public void increase() throws InterruptedException {
   
            value.set(10);
            Thread.sleep(10);
            System.out.println("increase value: " + value.get());
        }

        public void decrease() throws InterruptedException {
   
            value.set(-10);
            Thread.sleep(10);
            System.out.println("decrease value: " + value.get());
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        final Number number = new Number();
        Thread increaseThread = new Thread(new Runnable() {
   
            @Override
            public void run() 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值