【18】ThreadLocal深度剖析

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

ThreadLocal

1.ThreadLocal是什么?

(1)线程本地变量,有些地方也叫做线程本地存储。
(2)ThreadLocal可以让每个线程拥有一个属于自己的变量的副本,不会和其他线程的变量副本冲突,实现了线程的数据隔离。

在这里插入图片描述

1.1使用ThreadLocal实现线程数据隔离案例

public class UseThreadLocal {

    static ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
    static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();

    //自定义ThreadLoacal使用
    //static MyThreadLocal<String> threadLocal1 = new MyThreadLocal<>();


    /**
     * 1.测试线程
     * 1.1线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
     */
    public static class PersonThread implements Runnable{
        private int id;

        public PersonThread(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            threadLocal1.set("线程"+id);
            /*
               1.当前线程为线程1的时候,为线程1再添加一个数据,即为线程1的Entry[]数组中添加两个元素数据
               2.而其他线程的Entry[]数组只放1个元素。
               3.以此证明利用ThreadLocal可以实现线程数据隔离,每个线程只操作自己内部的数据,不会影响
               到其他线程的数据。
             */
            if(id == 1){
                threadLocal2.set(id);;
            }
            System.out.println(String.format("%s:%s",threadName,threadLocal1.get()));
        }
    }

    public void startThreadArray(){
        Thread[] runs = new Thread[3];
        //1.创建线程对象并放入线程数组
        for(int i = 0 ; i < runs.length;i++){
            runs[i] = new Thread(new PersonThread(i));
        }

        //2.取出线程对象并启动线程
        for (int i = 0 ; i < runs.length;i++){
            runs[i].start();
        }
    }

    public static void main(String[] args) {
        UseThreadLocal useThreadLocal = new UseThreadLocal();
        useThreadLocal.startThreadArray();
    }
}

2.自定义ThreadLocal实现线程数据隔离

/**
 * 自己实现的ThreadLocal
 * @param <T>
 */
public class MyThreadLocal<T> {

    /**
     * 存放变量副本的map容器,以Thread为键,变量副本为value
     */
    private Map<Thread,T> threadMap = new HashMap<>();

    /**
     * 将当前线程传进去拿到值
     * @return
     */
    public synchronized T get(){
        return threadMap.get(Thread.currentThread());
    }

    /**
     * 1.以当前线程为键,以传入的值为value,保存这个线程到map容器中。
     * 2.为确保线程安全,所有的方法都加上synchronized关键字。
     * @param t
     */
    public synchronized void set(T t){
        threadMap.put(Thread.currentThread(),t);
    }
}

3.自定义ThreadLocal需要面对的核心问题

(1)即在存放变量副本的容器Map<Thread,T> threadMap上面将会出线激烈的竟争。
(2)就如同抢蓝球,这个竟争是非常的激烈的。
(3)使用ThreadLocal就相当于给每一个人发一个蓝球,就相于把所有的蓝球编上号放到一个柜子里面,谁要用,去拿到这个柜子里面的蓝球,用完了又放回这个柜子里面,虽然说这些人不抢蓝球了,但是会抢这个柜子的钥匙。所以这个时候,它对于大的容器上面还是有竟争。所以JDK在内部实现的时候,它并没有按我们自定义的方式进行实现。

4.JDK中是如何实现ThreadLocal的线程数据隔离的

(1)ThreadLocal的set方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

(2)哪个线程调用了set方法,就将当前线程作为参数传入一个getMap()方法,返回一个ThreadLocalMap
(3)getMap

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

(4)ThreadLocalMap是当前线程Thread类中的成员属性。

ThreadLocal.ThreadLocalMap threadLocals = null;

(5)ThreadLocalMap的定义

  • 它是在ThreadLocal中定义的
  • 在ThreadLocalMap中定义了一个Entry[] table的数组。
private Entry[] table;
  • 这个Entry是ThreadLocal作为键,Object作为value.
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

(6)类的引用关系

public class Thread implements Runnable{
	ThreadLocalMap threadLocals;
}

static class ThreadLocalMap{
	private Entry[] table;
}

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
		}
}

(7)调用ThreadLocal的set方法的时候,会发生什么事情呢?

  • 首先线程已经有了。
  • 在每一个线程的内部产生一个ThreadLocalMap对象实例
  • 在ThreadLocalMap对象中有一个Entry[]数组类型的成员属性
  • 当调用set方法往Entry[]中set的时候,就会放入元素。它的键就是threadLocal,而值是线程id。
  • 为什么要用Entry[]数组?

ThreadLocal不会用一个容器来存,而是在Thread的内部,自己来保存变量的副本,因为一个线程是允许有多个ThreadLocal型的变量,所以它用了一个Entry[]数组来保存键为ThreadLoacal,值为Object的元素,由此实现了线程之间数据的完全隔离。

每一个线程访问自己的数据。

(8)在任何并发工具里面,ThreadLocal的性能一直是最强大的。

  • 原因就是不需要跟别人去抢占资源了,所以性能就会得到大大的提高。

  • 什么叫竞争?

就是多个线程抢一个东西叫竞争。

5.打赏鼓励

感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!

5.1微信打赏

在这里插入图片描述

5.2支付宝打赏

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值