借助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面向对象编程文章查看此处

相关文章
|
12月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
609 1
|
12月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
540 1
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
525 0
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
706 16
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
存储 SQL 安全
Java共享问题 、synchronized 线程安全分析、Monitor、wait/notify以及锁分类
Java共享问题 、synchronized 线程安全分析、Monitor、wait/notify以及锁分类
365 0
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
安全 Java API
Java 8中的Stream API:简介与实用指南深入理解Java并发编程:线程安全与锁优化
【5月更文挑战第29天】本文旨在介绍Java 8中引入的Stream API,这是一种用于处理集合的新方法。我们将探讨Stream API的基本概念,以及如何使用它来简化集合操作,提高代码的可读性和效率。 【5月更文挑战第29天】 在Java并发编程中,线程安全和性能优化是两个核心议题。本文将深入探讨如何通过不同的锁机制和同步策略来保证多线程环境下的数据一致性,同时避免常见的并发问题如死锁和竞态条件。文章还将介绍现代Java虚拟机(JVM)针对锁的优化技术,包括锁粗化、锁消除以及轻量级锁等概念,并指导开发者如何合理选择和使用这些技术以提升应用的性能。

热门文章

最新文章