自学内容网 自学内容网

ThreadLocal的使用

1、介绍

从java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文我们可以得知 ThreadLocal 的作用是:提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。

总结:
1.线程并发: 在多线程并发的场景下
2.传递数据: 我们可以通过ThreadLoca1在同一线程,不同组件中传递公共变量
3.线程隔离: 每个线程的变量都是独立的,不会互相影响

2、基本方法

3、使用案例

3.1、线程不隔离的场景

线程不隔离的场景:

        // ------------------------------
        //------------------------------
        //线程2--->线程3的数据
        //------------------------------
        //------------------------------
        //线程0--->线程2的数据
        //------------------------------
        //线程3--->线程4的数据
        //线程4--->线程4的数据
        //线程1--->线程4的数据
        // 由于java是抢占式的调度

线程拿到了不属于自己线程的数据,没有实现隔离

package com.hspedu.spring.threadLocal;

/**
 * @Author: lihaojie
 * @Description: 需求-线程隔离 每个线程中的变量都是互相独立的 线程没有隔离的现象
 * @DateTime: 2024/4/18 13:42
 **/
public class MyDemo01 {

    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public static void main(String[] args) {

        // ------------------------------
        //------------------------------
        //线程2--->线程3的数据
        //------------------------------
        //------------------------------
        //线程0--->线程2的数据
        //------------------------------
        //线程3--->线程4的数据
        //线程4--->线程4的数据
        //线程1--->线程4的数据
        // 由于java是抢占式的调度

        MyDemo01 demo = new MyDemo01();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println("------------------------------");
                    System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }

    }

}

3.2、使用ThreadLocal实现线程隔离

        // ------------------------------
        //------------------------------
        //线程4--->线程4的数据
        //------------------------------
        //------------------------------
        //线程2--->线程2的数据
        //------------------------------
        //线程3--->线程3的数据
        //线程0--->线程0的数据
        //线程1--->线程1的数据    已经解决了线程隔离的问题

package com.hspedu.spring.threadLocal;

/**
 * @Author: lihaojie
 * @Description: 需求-线程隔离 每个线程中的变量都是互相独立的 使用threadLocal解决
 * @DateTime: 2024/4/18 13:42
 **/

/**
 * ThreadLocal:
 *  1. set(): 将变量绑定到当前线程中
 *  2. get(): 获取当前线程绑定的变量
 *  3.
 */
public class MyDemo02 {

    ThreadLocal<String> t1 = new ThreadLocal<>();

    private String content;

    public String getContent() {
        // return content;
        return t1.get();
    }

    public void setContent(String content) {
        // this.content = content;
        // 绑定到当前线程
        t1.set(content);
    }

    public static void main(String[] args) {

        // ------------------------------
        //------------------------------
        //线程4--->线程4的数据
        //------------------------------
        //------------------------------
        //线程2--->线程2的数据
        //------------------------------
        //线程3--->线程3的数据
        //线程0--->线程0的数据
        //线程1--->线程1的数据    已经解决了线程隔离的问题

        MyDemo02 demo = new MyDemo02();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println("------------------------------");
                    System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }

    }

}

3.3、使用Synchronized解决线程隔离的问题

package com.hspedu.spring.threadLocal;

/**
 * @Author: lihaojie
 * @Description: 需求-线程隔离 每个线程中的变量都是互相独立的 使用Synchronized加锁来实现线程隔离
 * @DateTime: 2024/4/18 13:42
 **/

/**
 * ThreadLocal:
 *  1. set(): 将变量绑定到当前线程中
 *  2. get(): 获取当前线程绑定的变量
 *  3.
 */
public class MyDemo03 {

    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public static void main(String[] args) {

        MyDemo03 demo = new MyDemo03();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 加锁
                    synchronized (MyDemo03.class) {
                        demo.setContent(Thread.currentThread().getName() + "的数据");
                        System.out.println("------------------------------");
                        System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
                    }
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }

    }

}

3.4、ThreadLocal与Synchronized的区别

4、运用场景-事务案例

事务的使用注意点
1.service层和dao层的连接对象保持一致
2.每个线程的connection对象必须前后一致,线程隔离

常规的解决方案:

1、传参,将service层的connection对象传递到dao层

2、加锁

这样做虽然可以解决问题,但是它 提高了代码的耦合度 降低了程序的性能

3、使用ThreadLocal解决

原本:直接从连接池中获取连接
现在:
1.直接获取当前线程绑定的连接对象
如果连接对象是空的
2 .
2.1再去连接池中获取连接
2.2将此连接对象跟当前线程进行绑定

5、ThreadLocal的内部结构

常见的误解:

现在的设计:

1、每个Map存储的entry数量变少,因为之前是每个线程都是map里的一条数据,现在是只有threadLocal才是map里的一条数据,threadLocal的数量肯定是比线程的总数量少的

2、现在放在Thread里,随着Thread的销毁,ThreadLocal也会随之销毁掉,减少了内存的使用,之前并不会随着Thread的销毁而销毁

6、ThreadLocal核心方法源码

6.1、set方法

6.2、get方法

6.3、remove方法

6.4、initialValue方法

7、ThreadLocalMap

7.1、基本结构

8、内存泄露-key为强/弱引用

 


原文地址:https://blog.csdn.net/weixin_45768501/article/details/137919179

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!