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了。

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

相关文章
|
8天前
|
安全 Java 数据处理
Java并发编程:解锁多线程的潜力
在数字化时代的浪潮中,Java作为一门广泛使用的编程语言,其并发编程能力是提升应用性能和响应速度的关键。本文将带你深入理解Java并发编程的核心概念,探索如何通过多线程技术有效利用计算资源,并实现高效的数据处理。我们将从基础出发,逐步揭开高效并发编程的面纱,让你的程序运行得更快、更稳、更强。
|
7天前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
28 7
|
16天前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
49 0
|
1天前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
|
8天前
|
缓存 Java 数据处理
Java中的并发编程:解锁多线程的力量
在Java的世界里,并发编程是提升应用性能和响应能力的关键。本文将深入探讨Java的多线程机制,从基础概念到高级特性,逐步揭示如何有效利用并发来处理复杂任务。我们将一起探索线程的创建、同步、通信以及Java并发库中的工具类,带你领略并发编程的魅力。
|
11天前
|
安全 算法 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
51 6
|
11天前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(中)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
53 5
|
10天前
|
Java 调度 开发者
Java并发编程:解锁多线程同步的奥秘
在Java的世界里,并发编程是提升应用性能的关键所在。本文将深入浅出地探讨Java中的并发工具和同步机制,带领读者从基础到进阶,逐步掌握多线程编程的核心技巧。通过实例演示,我们将一起探索如何在多线程环境下保持数据的一致性,以及如何有效利用线程池来管理资源。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你对Java并发编程有更深入的理解和应用。
|
11天前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
46 3
|
16天前
|
Java API 开发者
Java中的并发编程:解锁多线程的潜力
在数字化时代的浪潮中,并发编程已成为软件开发的核心技能之一。本文将深入探讨Java中的并发编程概念,通过实例分析与原理解释,揭示如何利用多线程提升应用性能和响应性。我们将从基础的线程创建开始,逐步过渡到高级的同步机制,并探讨如何避免常见的并发陷阱。读者将获得构建高效、稳定并发应用所需的知识,同时激发对Java并发更深层次探索的兴趣。
27 2