ThreadLocal类以及应用技巧

我们完全可以用ThreadLocal来完成线程范围内数据的共享。

package com.learn.day05.demo05;

import java.util.Random;

public class ThreadScopeShareData {
    // 定义一个ThreadLocal
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);
                    threadLocal.set(data);//直接往threadLocal里面里面扔数据即可
                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            System.out.println("A get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么关键字,它直接从当前线程中取
        }
    }

    static class TestB {
        public void getData() {
            System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么关键字,它直接从当前线程中取
        }
    }
}

可以看出,其实ThreadLocal就相当于一个Map,只不过我们不需要设定key了,它默认就是当前的Thread,往里面放数据,直接set即可,取数据,直接get即可,很方便,就不用Map一个个存了,但是问题来了,ThreadLocal虽然存取方便,但是get()方法中根本没有参数,也就是说我们只能往ThreadLocal中放一个数据,多了就不行了,那么该如何解决这个问题呢?
  很明显,ThreadLocal是个容器,且只能存一下,那么如果有多个数据,我们可以定义一个类,把数据都封装到这个类中,然后扔到ThreadLocal中,用的时候取这个类,再从类中去我们想要的数据即可。
好,现在有两个线程,每个线程都要操作各自的数据,而且数据有两个:名字和年龄。根据上面的思路,写一个demo,如下:
 

package com.learn.day05.demo05;

import java.util.Random;

public class ThreadScopeShareData2 {

    private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) {//开启两个线程
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);

                    //每个线程中维护一个User,User中保存了name和age
                    User user = new User();
                    user.setName("name" + data);
                    user.setAge(data);
                    threadLocal.set(user); //向当前线程中存入user对象

                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {

            User user = threadLocal.get();//从当前线程中取出user对象
            System.out.println("A get data from " + Thread.currentThread().getName() + ": "
                    + user.getName() + "," + user.getAge());
        }
    }

    static class TestB {
        public void getData() {

            User user = threadLocal.get();//从当前线程中取出user对象
            System.out.println("B get data from " + Thread.currentThread().getName() + ": "
                    + user.getName() + "," + user.getAge());

        }
    }

}
//定义一个User类来存储姓名和年龄
class User {

    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

这样进行一下封装就可以实现多个数据的存储了,但是上面这个程序是不太好的,原因很明显,在线程中,我要自己new一个对象,然后对其进行操作,最后还得把这个对象扔到当前线程中。这不太符合设计的思路,设计的思路应该是这样的,不能让用户自己去new啊,如果有个类似于getThreadInstance()的方法,用户想要从ThreadLocal中拿什么对象就用该对象去调用这个getThreadInstance()方法多好,这样拿到的永远都是本线程范围内的对象了。
  这让我想到了学习JDBC的时候,从ThreadLocal中拿connection时的做法了,如果当前ThreadLocal中有就拿出来,没有就产生一个,这跟这里的需求是一样的,我想要一个User,那我应该用User去调用getThreadLInstance()方法获取本线程中的一个User对象,如果有就拿,如果没有就产生一个。完全一样的思路。这个设计跟单例的模式有点像,这里说有点像不是本质上像,是代码结构很像。先看一下简单的单例模式代码结构:
 

package com.learn.day05.demo05;

public class Singleton {
    private static Singleton instance = null;
    private Singleton() {//私有构造方法阻止外界new
    }
    public static synchronized Singleton getInstance() {  //提供一个公共方法返回给外界一个单例的实例
        if (instance == null) {  //如果没有实例
            instance = new Singleton();  //就新new一个
        }
        return instance;  //返回该实例
    }
}

这是懒汉式单例模式的代码结构,我门完全可以效仿该思路去设计一个从当前线程中拿User的办法,所以将程序修改如下

package com.learn.day05.demo05;

import java.util.Random;

public class ThreadScopeShareData5 {
//不需要在外面定义threadLocal了,放到User类中了
//  private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; i ++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);

                    //这里直接用User去调用getThreadLocal这个静态方法获取本线程范围内的一个User对象
                    //这里就优雅多了,我完全不用关心如何去拿该线程中的对象,如何把对象放到threadLocal中
                    //我只要拿就行,而且拿出来的肯定就是当前线程中的对象,原因看下面User类中的设计
                    User2.getThreadInstance().setName("name" + data);
                    User2.getThreadInstance().setAge(data);

                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            //还是调用这个静态方法拿,因为刚刚已经拿过一次了,threadLocal中已经有了
            User2 user = User2.getThreadInstance();
            System.out.println("A get data from " + Thread.currentThread().getName() + ": "
                    + user.getName() + "," + user.getAge());
        }
    }

    static class TestB {
        public void getData() {

            User2 user = User2.getThreadInstance();
            System.out.println("A get data from " + Thread.currentThread().getName() + ": "
                    + user.getName() + "," + user.getAge());
        }
    }

}

class User2 {

    private User2() {}

    private static ThreadLocal<User2> threadLocal = new ThreadLocal<User2>();

    //注意,这不是单例,每个线程都可以new,所以不用synchronized,
    //但是每个threadLocal中是单例的,因为有了的话就不会再new了
    public static /*synchronized*/ User2 getThreadInstance() {
        User2 instance = threadLocal.get(); //先从当前threadLocal中拿
        if(instance == null) {
            instance = new User2();
            threadLocal.set(instance);//如果没有就新new一个放到threadLocal中
        }
        return instance; //向外返回该User
    }

    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

 经过这样的改造,代码就优雅多了,外界从来不要考虑如何去当前线程中拿数据,只要拿就行,拿出来的肯定就是当前线程中你想要的对象,因为在对象内部已经写好了这个静态方法了,而且拿出来操作完了后,也不需要再放到threadLocal中,因为它本来就在threadLocal中,这就封装的相当好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于多线程编程,ThreadLocal是一个非常有用的工具。它提供了线程本地变量的机制,使得每个线程都可以独立地存储和获取自己的变量副本。这在一些场景下非常有用,下面是一些ThreadLocal应用技巧: 1. 线程上下文信息传递:在多线程环境中,有时需要在线程之间传递一些上下文信息,例如用户身份认证信息、语言环境等。使用ThreadLocal可以很方便地实现这一目的,每个线程都有自己独立的上下文信息副本,互不干扰。 2. 线程安全的数据存储:在多线程环境中,如果多个线程需要共享一份数据,可以使用ThreadLocal来维护每个线程的局部副本。这样每个线程都可以独立地修改自己的副本,而不会影响其他线程的数据。 3. 避免传参:有些情况下,我们需要在多个方法之间传递某些参数,但是参数传递会增加代码复杂性。使用ThreadLocal可以避免传参,每个方法可以直接从ThreadLocal中获取需要的参数值。 4. 事务管理:在一些事务场景中,我们可能需要在同一个线程中执行多个数据库操作,并保证这些操作在同一个数据库事务中。使用ThreadLocal可以在线程中维护一个数据库连接对象,确保多个操作都使用同一个连接,实现事务的一致性。 需要注意的是,ThreadLocal虽然提供了线程本地变量的机制,但并不是万能的解决方案。在使用ThreadLocal时,需要注意内存泄漏的问题,及时清理不再使用的资源。 希望以上内容对您有帮助!如需了解更多多线程编程相关内容,可以参考CSDN中张孝祥的相关文章。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值