Java——多线程高并发系列之ThreadLocal的使用

简介: Java——多线程高并发系列之ThreadLocal的使用

文章目录:


写在前面

Demo1

Demo2

Demo3

写在前面


除了控制资源的访问外,还可以通过增加资源来保证线程安全ThreadLocal 主要解决为每个线程绑定自己的值。

Demo1


package com.szh.threadlocal;
/**
 * ThreadLocal 的基本使用
 */
public class Test01 {
    //定义一个ThreadLocal对象
    static ThreadLocal threadLocal=new ThreadLocal();
    //定义线程类
    static class SubThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                //设置线程关联的值
                threadLocal.set(Thread.currentThread().getName() + " ---> " + i);
                //读取线程关联的值
                System.out.println(Thread.currentThread().getName() + " value ---> " + threadLocal.get());
            }
        }
    }
    public static void main(String[] args) {
        SubThread t1=new SubThread();
        SubThread t2=new SubThread();
        t1.start();
        t2.start();
    }
}

这里定义了两个子线程,之后我为每个子线程都设置了5个关联的值(set方法),然后可以通过(get方法)获取到为每个子线程设置的关联值。


Demo2


package com.szh.threadlocal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 在多线程环境中,把字符串转换为日期对象,多个线程使用同一个 SimpleDateFormat 对象
 * 可能会产生线程安全问题,有异常
 * 为每个线程指定自己的 SimpleDateFormat 对象, 使用 ThreadLocal
 */
public class Test02 {
    //定义ThreadLocal对象
    private static ThreadLocal<SimpleDateFormat> threadLocal=new ThreadLocal<>();
    static class ParseDate implements Runnable {
        private int i=0;
        public ParseDate(int i) {
            this.i=i;
        }
        @Override
        public void run() {
            String test="2021年6月8日 13:14:" + i%60; //构建日期字符串
            //先判断当前线程是否有 SimpleDateFormat 对象,如果当前线程没有 SimpleDateFormat 对象就创建一个,如果有就直接使用
            if (threadLocal.get() == null) {
                threadLocal.set(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"));
            }
            try {
                Date date=threadLocal.get().parse(test);
                System.out.println(i + " -- " + date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        //创建10个线程,每个线程都有自己对应的日期
        for (int i = 0; i < 10; i++) {
            new Thread(new ParseDate(i)).start();
        }
    }
}

ThreadLocalnew对象的时候,是可以有一个泛型的,这里我将泛型定义为SimpleDateFormat 这个日期格式化类。然后我创建了10个子线程,为每个子线程都设置一个它们各自的时间。

那么这里先判断当前线程是否有SimpleDateFormat 对象,如果当前线程没有SimpleDateFormat 对象就创建一个;如果有就直接调用get方法使用(get方法返回的是一个泛型T),所以这里调用get方法返回的就是 SimpleDateFormat,之后再通过parsetest字符串转为日期。

Demo3


package com.szh.threadlocal;
import java.util.Date;
/**
 * ThreadLocal 初始值, 第一次调用threadLocal的get()方法会返回null
 * 解决方法:
 *      定义 ThreadLocal 类的子类,
 *      在子类中重写 initialValue() 方法指定初始值,再第一次调用 get()方法不会返回 null
 */
public class Test03 {
    //定义ThreadLocal
    static ThreadLocal threadLocal=new SubThreadLocal();
    //定义ThreadLocal的子类,同时重写 initialValue() 方法
    static class SubThreadLocal extends ThreadLocal<Date> {
        @Override
        protected Date initialValue() {
            //将初始值设置为15分钟之前(单位是ms,1000ms=1s,*60=1分钟,*15=15分钟)
            return new Date(System.currentTimeMillis() - 1000*60*15);
        }
    }
    //定义线程类
    static class SubThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("-----" + Thread.currentThread().getName() + " value= "
                                + threadLocal.get());
                //如果没有初始值,就设置当前日期
                if (threadLocal.get() == null) {
                    threadLocal.set(new Date());
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        SubThread t1=new SubThread();
        SubThread t2=new SubThread();
        t1.start();
        t2.start();
    }
}

将上面代码中的静态内部类注释掉,然后将代码的第14行修改为:static ThreadLocal threadLocal=new ThreadLocal(); 就会出现下面这张图的运行结果。

原因是在最初的时候,如果当前线程的ThreadLocal对象没有set了话,那么get到的值将会是null

上面那张图是不是最开始的valuenull,那么解决方法就是:定义一个ThreadLocal的子类,然后重写 initialValue 方法,在这里面指定初始值,之后再调用get方法就不会得到null了。

下面这张图是上面完整代码的运行结果,可以看到为这两个子线程分别设定了一个指定的时间。

目录
打赏
0
0
0
0
85
分享
相关文章
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
42 5
线程池在高并发下如何防止内存泄漏?
线程池在高并发下如何防止内存泄漏?
210 6
Java 线程池在高并发场景下有哪些优势和潜在问题?
Java 线程池在高并发场景下有哪些优势和潜在问题?
101 2
Java高并发处理机制
Java高并发处理机制
53 1
|
5月前
|
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
152 2
JAVA并发编程系列(12)ThreadLocal就是这么简单|建议收藏
很多人都以为TreadLocal很难很深奥,尤其被问到ThreadLocal数据结构、以及如何发生的内存泄漏问题,候选人容易谈虎色变。 日常大家用这个的很少,甚至很多近10年资深研发人员,都没有用过ThreadLocal。本文由浅入深、并且才有通俗易懂方式全面分析ThreadLocal的应用场景、数据结构、内存泄漏问题。降低大家学习啃骨头的心理压力,希望可以帮助大家彻底掌握并应用这个核心技术到工作当中。
java高并发场景RabbitMQ的使用
java高并发场景RabbitMQ的使用
177 0
Kafka多线程Consumer是实现高并发数据处理的有效手段之一
【9月更文挑战第2天】Kafka多线程Consumer是实现高并发数据处理的有效手段之一
506 4
如何提高 Java 高并发程序的性能?
以下是提升Java高并发程序性能的方法:优化线程池设置,减少锁竞争,使用读写锁和无锁数据结构。利用缓存减少重复计算和数据库查询,并优化数据库操作,采用连接池和分库分表策略。应用异步处理,选择合适的数据结构如`ConcurrentHashMap`。复用对象和资源,使用工具监控性能并定期审查代码,遵循良好编程规范。
|
7月前
|
Java 中的 ThreadLocal 变量
【8月更文挑战第22天】
61 4

热门文章

最新文章