借助ThreadLocal实现多线程安全 | 带你学《Java语言高级特性》之三十二

简介: 本节结合消息发送案例简单直白地向读者展现了多线程模式下的线程安全问题,并向读者介绍了ThreadLocal类及其实现线程安全的原理,讲解了其使用方法。

上一篇:教你使用UUID、Optional类 | 带你学《Java语言高级特性》之三十一

本节结合消息发送案例简单直白地向读者展现了多线程模式下的线程安全问题,并向读者介绍了ThreadLocal类及其实现线程安全的原理,讲解了其使用方法。

【本节目标】
通过阅读本节内容,你将了解到多线程模式下重复性业务的安全问题和ThreadLocal能够实现线程安全的原理,学会使用ThreadLocal类来解决开发过程中的线程安全问题。

ThreadLocal类

在真正去了解ThreadLocal类作用的时候下面编写一个简单的程序做一个先期的分析。

范例:现在定义这样一个结构

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Message msg=new Message();   //实例化消息主体对象
        msg.setInfo("www.mldn.cn");     //设置要发送的内容
        Channel.setMessage(msg);    //设置要发送的消息
        Channel.send();    //发送消息
    }
}
class Channel {  //消息的发送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {   //发送消息
        System.out.println("【消息发送】" + message.getInfo());
    }   //【消息发送】www.mldn.cn
}
class Message {    //要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}

image.png
ThreadLocal类

对于当前的程序实际上采用的是一种单线程的模式来进行处理的;那么如果在多线程的状态下能否实现完全一致的操作效果呢?为此将启动三个线程进行处理。

范例:多线程的影响

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第一个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者A").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第二个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者B").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第三个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者C").start();
    }
}
class Channel {//消息的发送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {//发送消息
        System.out.println("【"+Thread.currentThread().getName()+"、消息发送】" + message.getInfo());
    }
}
class Message {//要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
 * 【消息发送者A、消息发送】第二个线程的消息
 * 【消息发送者B、消息发送】第二个线程的消息
 * 【消息发送者C、消息发送】第三个线程的消息
 */

这个时候消息的处理产生了影响。

image.png
考虑多线程ThreadLocal类

在保持Channel(所有发送的通道)核心结构不改变的情况下,需要考虑到每个线程的独立操作问题。那么在这样的情况下,发现对于Channel类而言,除了要保留有发送的消息之外,还应该多存放一个每一个线程的标记(当前线程),这个时候可以通过ThreadLocal类来存放数据。在ThreadLocal类里面提供有如下操作方法:

  • 构造方法:public ThreadLocal();
  • 设置数据:public void set(T value);
  • 取出数据:public T get();
  • 删除数据:public void remove();

image.png
解决同步问题

范例:解决线程同步问题

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第一个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者A").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第二个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者B").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第三个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者C").start();
    }
}
class Channel {   //消息的发送通道
   private Channel(){}
   public static final ThreadLocal<Message> THREADLOCAL=new ThreadLocal<Message>();
   public static void setMessage(Message m) {
       THREADLOCAL.set(m);    //向ThreadLocal中保存数据
   }
   public static void send() {  //发送消息
       Message message=THREADLOCAL.get();
       System.out.println("【"+Thread.currentThread().getName()+"、消息发送】" + THREADLOCAL.get().getInfo());
   }
}
class Message {//要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
* 【消息发送者B、消息发送】第二个线程的消息
* 【消息发送者C、消息发送】第三个线程的消息
* 【消息发送者A、消息发送】第一个线程的消息
*/

每一个线程通过ThreadLocal只允许保存一个数据。

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

下一篇:手把手教你创建定时任务 | 带你学《Java语言高级特性》之三十三
更多Java面向对象编程文章查看此处

相关文章
|
6天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
37 6
|
15天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
15天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
16天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
19天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
28 2
|
19天前
|
监控 Java 开发者
Java线程管理:守护线程与本地线程的深入剖析
在Java编程语言中,线程是程序执行的最小单元,它们可以并行执行以提高程序的效率和响应性。Java提供了两种特殊的线程类型:守护线程和本地线程。本文将深入探讨这两种线程的区别,并探讨它们在实际开发中的应用。
27 1
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
23 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2
下一篇
DataWorks