ThreadLocal 的使用

本文详细介绍了ThreadLocal类在Java中的使用方法,包括解决线程间变量隔离的问题,避免共享变量冲突,以及如何设置默认值来避免get方法返回null等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ThreadLocal 的使用

变量值的共享可以使用 public static 变量的形式,所有的线程都使用同一个 public static 变量。如果想实现每一个线程都有自己的共享变量该如何解决?JDK 中提供的类 ThreadLocal 正是为了解决这样的问题。

类 Thread Local 主要为了解决的就是每个线程绑定自己的值,可以将 ThreadLocal 类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据

方法 get() 和 null

public class ThreadLocal_Demo1 {
    public static ThreadLocal t1 = new ThreadLocal();

    public static void main(String[] args) {
        if(t1.get() == null){
            System.out.println("从未放过值");
            t1.set((int)(Math.random()*10));
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

image_1at3lhug7q4d1imqa6td6415f39.png-6.5kB

从上图中可以看到,第一次调用 t1 对象的 get() 方法时返回值是 null,通过调用 set() 方法赋值将值打印在控制台上。类 ThreadLocal 解决的是变量在不同线程间的隔离性,也就是不同的线程拥有自己的值,不同线程中的值是可以放入 ThreadLocal 中进行保存的。

验证线程隔离性

创建测试用的项目 ThreadLocalTest,类 Tools.java 代码如下
Tools 代码

public class Tools {
    public static ThreadLocal t1 = new ThreadLocal();
}

ThreadA 代码

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                Tools.t1.set("ThreadA "+i);
                System.out.println("Thread A get Value "+ Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadB 代码

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                Tools.t1.set("ThreadB "+i);
                System.out.println("Thread B get Value "+ Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

主线程代码

public class mainThread {
    public static void main(String[] args) {
        try {
            Thread A = new ThreadA();
            Thread B = new ThreadB();
            A.start();
            B.start();
            for (int i = 0; i < 5; i++) {
                Tools.t1.set("main "+ i);
                System.out.println("Main get Value = " + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

image_1at3mb5rmnpt1c6p1cuut3okrq13.png-15.4kB

程序运行结果如上所示。

虽然 3 个线程都向 t1 对象中 set() 数据值,但每个线程还是能够取出自己的数据。

创建新的项目,我们重新验证一次数据的隔离性
Tools 代码

import java.util.Date;

public class Tools {
    public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();
}

ThreadA 代码

import java.util.Date;

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                if(Tools.t1.get() == null){
                    Tools.t1.set(new Date());
                }
                System.out.println("Thread A get Value "+ Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadB 代码

import java.util.Date;

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                if(Tools.t1.get() == null){
                    Tools.t1.set(new Date());
                }
                System.out.println("Thread B get Value "+ Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

main 线程代码

public class mainThread {
    public static void main(String[] args) {
        try {
            Thread A = new ThreadA();
            Thread B = new ThreadB();
            A.start();
            B.start();
            for (int i = 0; i < 5; i++) {
                if(Tools.t1.get() == null){
                    Tools.t1.set(new Date());
                }
                System.out.println("Main get Value = " + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

image_1at3n7ff5m5f1uuuj1n3ndj6d1g.png-45.4kB

如上所示,虽然数据隔离性方面没有问题,但是每一次设置值的时候都要进行非空判定是一件令人烦恼的事情,那么我们要如何解决呢?

解决 get() 返回 null 问题

创建名称为 ThreadLcoal4 的项目,继承 ThreadLocal 类产生的 ThreadLocalExt 类
ThreadLocalExt 代码

public class ThreadLocalExt extends ThreadLocal {
    public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();

    protected Object initialValue() {
        return "我是默认值,第一次 get 的时候不再为空";
    }
}

main 线程代码

public class mainThread {
    public static ThreadLocalExt t1 = new ThreadLocalExt();
    public static void main(String[] args) {
        if(t1.get() == null){
            System.out.println("从未放过值");
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

image_1at3o62mgu42s701ob78qevl9.png-10.6kB

再次验证线程变量的隔离性

ThreadLocalExt

public class ThreadLocalExt extends ThreadLocal {
    public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();

    protected Object initialValue() {
        return new Date().getTime();
    }
}

Tools

public class ThreadLocalExt extends ThreadLocal {
    public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();

    protected Object initialValue() {
        return new Date().getTime();
    }
}

ThreadA

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++) {
                Tools.t1.set(new Date());
                System.out.println("Thread A get Value " + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadB

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++) {
                Tools.t1.set(new Date());
                System.out.println("Thread B get Value " + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

mainThread

public class mainThread {
    public static void main(String[] args) {
        try {
            Thread A = new ThreadA();
            Thread B = new ThreadB();
            A.start();
            B.start();
            for (int i = 0; i < 10000; i++) {
                Tools.t1.set(new Date());
                System.out.println("Main get Value = " + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

image_1at3oohpscoc168cnt1hq91cdc13.png-69.8kB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值