Java多线程编程-ThreadLocal和InheritableThreadLocal使用及其源码分析

Java多线程编程-ThreadLocal和InheritableThreadLocal使用及其源码分析


如果我们想为每一个线程都有自己的共享变量,那我们可以使用ThreadLocal,如果我们使用public static是所有线程都可以使用。

ThreadLocal使用

提供线程局部变量。这些变量与普通变量不同,因为每次访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段
只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)

ThreadLocal相关API

get()方法和set()方法

get()方法:返回该线程局部变量在当前线程副本中的值

public T get() {
	//获取当前线程
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

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);
}

简单试试这两个方法:

public class ThreadLocalV1Main {
    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        if (threadLocal.get() == null) {
            System.out.println("current thread the threadlocal is null " + Thread.currentThread().getName());
            threadLocal.set("set value :" + Thread.currentThread().getName());
        }
        System.out.println("value is " + threadLocal.get());
    }
}

运行结果如下:
在这里插入图片描述
我们可以通过set和get方法源码可以看到,获取当前对象中值,最终会存储到一个Map(ThreadLocalMap:ThreadLocal的静态内部类)中,这个是以线程为key,保存相应的值。因此可以通过线程获取值。因此每个线程中的值是每个线程特有的。
我们现在验证一下ThreadLocal在多线程下每个线程之间独立保存一份
对象类:

public class ThreadLocalObj {

    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

}

线程1:

public class ThreadLocalT1 extends Thread {

    public ThreadLocalT1(String name) {
        this.setName(name);
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
                ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
                System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程2:

public class ThreadLocalT2 extends Thread {

    public ThreadLocalT2(String name) {
        this.setName(name);
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
                ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
                System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

运行:

public class ThreadLocalV2Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadLocalT1 threadLocalT1 = new ThreadLocalT1("thread01");
        threadLocalT1.start();
        ThreadLocalT2 threadLocalT2 = new ThreadLocalT2("thread02");
        threadLocalT2.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
            ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
            System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
            Thread.sleep(1000);
        }
    }
}

运行结果:
在这里插入图片描述
有运行结果可以得出,三个线程(thread01,thread02和Main)都是取各自线程中的值。

initialValue()

我们上面使用get方法有可能为null,但是想ThreadLocal初始值不为null怎么处理?就可以使用这个方法了。
initialValue()方法:返回当前线程的这个线程局部变量的“初始值”

protected T initialValue() {
        return null;
    }

这个方法是一个protected ,我们通过子类从写一下这个方法。

public class SubThreadLocal extends ThreadLocal<String> {

    @Override
    protected String initialValue() {
        return "default value";
    }
}

运行类:

public class SubThreadLocalMain {

    public static SubThreadLocal threadLocal = new SubThreadLocal();
    public static void main(String[] args) {
        System.out.println(threadLocal.get());
    }
}

运行结果:
在这里插入图片描述
表明ThreadLocal设置成功,ThreadLocal提供了一个私有的setInitialValue供get方法使用

 private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

这里只也是通过initialValue方法设置的。

remove()

上面有设置和获取方法,作为保存在map中的对象,肯定是有remove方法,不然不让我删除多尴尬!
remove():删除此线程局部变量的当前线程值

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

我们使用看看:

public class SubThreadLocalMain {
    public static SubThreadLocal threadLocal = new SubThreadLocal();
    public static void main(String[] args) {
        System.out.println(threadLocal.get());
        threadLocal.set("this is a set value");
        System.out.println(threadLocal.get());
        threadLocal.remove();
        System.out.println(threadLocal.get());
    }
}

运行结果:
在这里插入图片描述
输出结果的先输出初始值,后面输出设置的值,在进行remove操作,在调用get获取的为默认值。

InheritableThreadLocal类

此类扩展了ThreadLocal来提供从父线程到子线程的值的继承:创建子线程时,子级将接收父级具有值的所有可继承线程局部变量的初始值。 通常,子级的价值观将与父级的价值观相同。 但是,通过覆盖此类中的childValue方法,可以使子级的值成为父级的任意函数。 当必须将在变量中维护的每个线程属性自动传输到创建的任何子线程时,可继承的线程局部变量将优先于普通线程局部变量使用。
源码:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     *在创建子线程时,根据父级值计算此可继承线程局部变量的子级初始值。在启动子级之前,从父线程中调用此方法。 此方法仅返回其输入参数,如果需要其他行为,则应重写此方法。
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * 获取与ThreadLocal关联的ThreadLocalMap 
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     创建与ThreadLocal关联的Map
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

我们来操作一下:

public class SubInheritableThreadLocal extends InheritableThreadLocal<String> {
    @Override
    protected String initialValue() {
        return "default value";
    }
}
public class InheritableThreadLocalObj {

    public static SubInheritableThreadLocal subInheritableThreadLocal = new SubInheritableThreadLocal();

}
public class InheritableThreadLocalT1 extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println("current thread name:" + currentThread().getName() + " value:" + InheritableThreadLocalObj.subInheritableThreadLocal.get());
                Thread.sleep(1000);
            }
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
    }
}
public class InheritableThreadLocalV1 {

    public static void main(String[] args) {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + InheritableThreadLocalObj.subInheritableThreadLocal.get());
            }
            Thread.sleep(1000);
            InheritableThreadLocalT1 inheritableThreadLocalT1 = new InheritableThreadLocalT1();
            inheritableThreadLocalT1.setName("thread01");
            inheritableThreadLocalT1.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
在这里插入图片描述
那我们如何修改子线程中的值呢?我们只需要重写childValue方法就好了。
修改的代码:

public class SubInheritableThreadLocal extends InheritableThreadLocal<String> {

    @Override
    protected String initialValue() {
        return "default value";
    }

    @Override
    protected String childValue(String parentValue) {
        return "this is a sub thread name value " + Thread.currentThread().getName();
    }
}

运行结果:
在这里插入图片描述
这个就是打印了子线程修改过的值。这里要注意一点,如果子线程在进行get调用的时候,父线程在set的时候,子线程获取的还是之前的旧值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值