01入门-ThreadLocal详解-并发编程(Java)

news2024/11/16 5:29:52

文章目录

    • 1 简介
    • 2 基本使用
      • 2.1 常用方法
      • 2.2 小案例
    • 3 ThreadLocal与Sycronized
    • 4 应用场景
      • 4.1 转账案例构建
      • 4.2 问题
      • 4.3 解决
    • 5 后记

1 简介

官方JDK源码关于ThreadLocal描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法)能保证各个线程的局部变量独立其他线程的局部变量。ThreadLocal实例通常都是某些类的私有静态内部成员,用于关联线程和现场上下文。

ThreadLocal的作用:提供线程内局部变量,不同现场间不会相互干扰;线程局部变量在线程的生命周期内起作用;减少同一个线程内多个函数或者组件之间一些共享变量传递的复杂性。

总结:

  • 线程并发:在多线程并发的场景下。
  • 传递数据:我们可以通过ThreadLocal在同一线程下,不同组件中传递公共变量。
  • 线程隔离:每个线程的变量都是独立的,不会相互影响。

2 基本使用

2.1 常用方法

方法声明描述
ThreadLocal()创建ThreadLocal对象
T get()获取当前线程绑定的局部变量
void set(T)设置当前线程绑定的局部变量
void remove()移除当前线程绑定的局部变量

2.2 小案例

  • 要求:对于一个类成员变量,每个线程各自获取或者设置自己的值即实现线程隔离;单个线程设置和获取同一个变量

初始代码如下:

/**
 * @author Administrator
 * @date 2022-12-04 13:13
 */
public class MyDemo {
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }


    public static void main(String[] args) throws InterruptedException {
        MyDemo myDemo = new MyDemo();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                myDemo.setContent(Thread.currentThread().getName() + "的数据");
                System.out.println("---------------");
                System.out.println(Thread.currentThread().getName() + "----->" + myDemo.getContent());
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}

// 测试结果
---------------
---------------
---------------
线程1----->线程2的数据
线程2----->线程2的数据
---------------
线程4----->线程4的数据
线程0----->线程4的数据
---------------
线程3----->线程3的数据

用ThreadLocal改造上述代码如下:

/**
 * @author Administrator
 * @date 2022-12-04 13:13
 */
public class MyDemo {
    private String content;
    ThreadLocal<String> tl = new ThreadLocal<>();

    public String getContent() {
//        return content;
        return tl.get();
    }

    public void setContent(String content) {
//        this.content = content;
        tl.set(content);
    }

    public void clear() {
        tl.remove();
    }

    public static void main(String[] args) throws InterruptedException {
        MyDemo myDemo = new MyDemo();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                myDemo.setContent(Thread.currentThread().getName() + "的数据");
                System.out.println("---------------");
                System.out.println(Thread.currentThread().getName() + "----->" + myDemo.getContent());
            });
            thread.setName("线程" + i);
            thread.start();
//            thread.join();
        }
//        System.out.println(myDemo.getContent());
        myDemo.clear();
    }
}

// 测试结果
---------------
---------------
---------------
线程0----->线程0的数据
线程1----->线程1的数据
---------------
线程4----->线程4的数据
---------------
线程3----->线程3的数据
线程2----->线程2的数据

3 ThreadLocal与Sycronized

上面的测试用例用Syncronized能不能解决呢?答案是肯定的,但是有没有区别呢,用例相对简单这里不在给出Syncronized实现,下面分析ThreadLocal和Syncronized的不同,如下表3-1所示:

SyncronizedThreadLocal
原理同步机制采用用时间换空间的方式,只提供一份变量,让不同线程排队访问ThreadLocal采用以空间换时间的方式,为每一个线程提供一份变量副本,从而实现同时访问而不相互干扰
侧重点多个线程访问共享资源的同步多个线程中让每个线程之间的数据相互隔离
效果共享资源一致性,性能损耗,失去并发性每个线程之间数据隔离,每个线程在不同方法之间数据的传递,一致性
  • 总结:在刚刚的案例中,虽然使用ThreadLocal和Syncronized都能解决问题,但是使用ThreadLocal更为合适,因为这样使程序拥有更高的并发性。

4 应用场景

通过以上内容,我们已经基本了解ThreadLocal的特点,下面我们 通过一个案例,来看下ThreadLocal的应用场景:事务操作。

4.1 转账案例构建

  • 构建转账场景:有一个数据表,有2个用户张三和李四,张三给李四转账。

  • 设计一些知识:mysql数据库,JDBC和c3p0连接池,maven项目。

  • 项目结构如下:在这里插入图片描述

  • 功能简介

    • AccountDao实现了转出、转入及关闭操作
    • AccountService:转账和释放资源
    • JdbcUtils:获取连接、事务提交、事务回滚、释放连接

4.2 问题

在多线程环境下转账我们要保证数据完整性或者一致性,比如张三账户1000,例如账户1000,无论如何转账,总的资产为2000,但是实际会遇到什么情况呢?

  • 由于异常原因程序未完整运行导致数据不完整
  • 多线程并发导致数据库不完整性

4.3 解决

对于第一种情况,我们需要开启事务。开启事务需要保证在多线程环境下,在一次完整转账操作中数据库连接为同一个,在多个操作下连接可共享。多次转账间数据库连接的隔离,不相互干扰。

对于第二种情况设计数据库加锁问题,这里我们不做讨论。

通过上面的讨论,我们可以使用ThreadLocal来解决第一种情况,具体涉及数据库连接获取的代码如下:

private static final ComboPooledDataSource ds = new ComboPooledDataSource();
/**
 * 获取连接
 *  1. 直接从当前线程获取绑定的连接
 *  2. 如果连接对象为空
 *      2.1 在去连接池去获取连接
 *      2.2 将此对象绑定到当前线程
 */
static ThreadLocal<Connection> tc = new ThreadLocal<>();

/**
 * 获取数据库连接
 * @return  数据库连接
 * @throws SQLException sql异常
 */
public static Connection getConnection() throws SQLException {
    Connection conn = tc.get();
    if (conn == null) {
        conn = ds.getConnection();
        tc.set(conn);
    }
    return conn;
}
  • 第一次获取连接为空的情况下
    • 从数据库连接池获取连接
    • 把连接对象绑定到当前线程
  • 非第一次直接当前线程获取本地变量

通过在转账中添加/0算术异常来模拟异常情况,测试结果如下:

java.lang.ArithmeticException: / by zero
	at com.gaogzhen.service.AccountService.transfer(AccountService.java:32)
	at com.gaogzhen.web.AccountWeb.main(AccountWeb.java:18)
转账失败
Account(id=1, name=张三, balance=1000.00)
Account(id=2, name=李四, balance=1000.00)

完成的代码见文章末尾代码仓库地址。

5 后记

如有问题,欢迎交流讨论。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/concurrent

参考:

[1]黑马程序员Java基础教程由浅入深全面解析threadlocal[CP/OL].2020-03-24.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/71061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【opensips】客户端的注册

opensips的注册能力 opensips可以通过registrar模块实现注册的能力&#xff0c; 所有的账户信息默认是在opensips的subscibe表中&#xff0c; 默认的subscibe表结构如上图&#xff0c; id是主键&#xff0c;username是账户名domain是opensips的域名password是密码email_addre…

ChatGPT 全网最新开通账号教程

&#x1f986;博主介绍&#xff1a;小黄鸭技术 &#x1f308;擅长领域&#xff1a;Java、实用工具、运维 &#x1f440; 系列专栏&#xff1a;&#x1f4e2;开发工具 Java之路 八股文之路 &#x1f4e7;如果文章写作时有错误的地方&#xff0c;请各位大佬指正&#xff0c;一起进…

掌握Redis的Sentinel哨兵原理,助你拿到25k的offer

— — | M1 |---------| R1 | | S1 | | S2 | — — Configuration: quorum 1 master宕机&#xff0c;s1和s2中只要有1个哨兵认为master宕机就可以进行切换&#xff0c;同时会在s1和s2中选举出一个执行故障转移. 但此时&#xff0c;需要majority&#xff0c;也就是大多数…

微信小程序开发笔记 进阶篇⑥——getPhoneNumber 获取用户手机号码(基础库 2.21.2 之后)

文章目录一、前言二、前端代码wxml三、前端代码js四、后端java五、程序流程六、参考一、前言 大部分微信小程序开发者都会有这样的需求&#xff1a;获取小程序用户的手机号码。但是&#xff0c;因为小程序用户的手机号码属于重要信息&#xff0c;为了安全&#xff0c;所以需要如…

细粒度图像分类论文研读-2022

文章目录TransFG: A Transformer Architecture for Fine-grained RecognitionAbstractintroductionMethodVision transformer as feature extractorImage SequentializationPatch EmbeddingTransFG ArchitecturePart Selection ModuleConstrastive feature learningViT-FOD&…

从理论走向实战,阿里高工熬夜整理出的 Spring 源码速成笔记太香了

不知道大家面试的时候有没有被问到过 Spring 相关问题&#xff08;循环依赖、事务、生命周期、传播特性、IOC、AOP、设计模式、源码&#xff09;&#xff1f;反正我这个小学弟前段时间就来私信我说自己面试挂在了 Spring 这一块。&#xff08;原谅我不厚道地笑了&#xff0c;如…

毕设选题推荐基于python的django框架的自媒体社推广平台系统

&#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设老哥&#x1f525; &#x1f496; 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; Java实战项目专栏 Python实…

安卓APP源码和设计报告——运动健身教学

实 验 报 告 课程名称 实验名称 指导教师 专业 班级 学号 姓名 目 录 一、设计背景31. 需求分析32. 课题研究的目的和意义3二、系统需求分析与开发环境31. 系统功能需求32.系统界面需求43.开发环境4三、系统设计4四、系统测试51.脑模拟器测试6五、总结与展望6六、重要…

YoloV7目标检测(Pytorch版)【详解】

文章目录一、网络结构1、总体网络结构&#xff08;backbone&#xff09;2、主干网络介绍&#xff08;backbone&#xff09;2.1 多分支模块堆叠2.2 下采样网络结构2.3 整个backbone代码3、FPN特征金字塔二、预测结果的解码一、网络结构 1、总体网络结构&#xff08;backbone&am…

【组件开发实践】云巧流程组件对接实践

1. 用户需求 假设A系统有如下员工请假审批流场景&#xff1a; 员工请假小于等于3天&#xff0c;只需主管直接审批&#xff1b;大于3天需要主管先审批&#xff0c;审批通过后再由二级主管进行审批。当员工请假审批流节后后&#xff0c;需要通知A系统进行业务处理&#xff08;例…

Tomcat的IO模型

Tomcat支持一下几种IO模型&#xff1a; 支持的IO模型 特点 BIO 同步阻塞式IO&#xff0c;每一个请求都会创建一个线程&#xff0c;对性能开销大&#xff0c;不适合高并发场景。 NIO 同步非阻塞式IO&#xff0c;基于多路复用Selector监测连接状态通知线程处理&#xff0c;…

<C++>多态

文章目录1. 概念2. 多态的定义和实现2.1 多态的构成条件2.2 虚函数2.3 虚函数的重写2.4 虚函数重写的两个例外&#xff1a;2.5 C11 override和final2.6 重载、覆盖(重写)、隐藏(重定义)的对比3. 抽象类3.1 概念3.2 接口继承和实现继承3. 多态的原理3.1 虚函数表3.2 多态的原理3…

S2SH小区物业管理系统计算机专业毕业论文java毕业设计网站

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 《S2SH小区物业管理系统》该项目采用技术&#xff1a;jsp struts2springhibernatecssjs等相关技术&#xff0c;项目含有源码、文档、配套开发软件…

java EE初阶 — wait 和 notify

文章目录1.wait 和 notify1.1 wait()方法1.2 notify()方法1.3 notifyAll()方法1.wait 和 notify 线程最大的问题是抢占式指向&#xff0c;随机调度。而写代码的时候&#xff0c;确定的东西会比较好。 于是就有程序猿发明了一些办法&#xff0c;来控制线程之间的执行顺序。 虽…

火山引擎 RTC 助力抖音百万并发“云侃球”

动手点关注干货不迷路1. 背景及技术挑战从电视看直播到手机电脑看直播&#xff0c;直播技术的发展让观众可以随时、随地观看自己喜欢的比赛&#xff0c;并且在看比赛时通过发送表情、发文字进行互动。但表情、文字承载的信息量较小、沟通效率低&#xff0c;我们无法像线下一起看…

一大波节日来袭,App Store节日营销请注意!

11 月已经过去&#xff0c;在过去的 11 月里&#xff0c;我们经历了万圣节、双 11、世界杯、感恩节、黑色星期五等非常重要的营销节点。 在新的 12 月&#xff0c;我们将迎来世界杯闭幕、双12、平安夜、圣诞节等重要营销机遇。在未来&#xff0c;我们还会迎来新春营销的重要机…

图形API学习工程(29):解决在shader文件中使用include的问题

工程GIT地址&#xff1a;https://gitee.com/yaksue/yaksue-graphics 无用的前言 看了下提交记录&#xff0c;这个工程上次更新已经是一年以前了。最近想想&#xff0c;还是应该回来再继续学学&#xff0c;暂且不论是否对工作有帮助&#xff0c;我在这个工程上获得的愉悦感相比…

Excel 是您最容易被忽视的设计工具 设计师对世界排名第一的电子表格工具的看法——如何构建信息图表、仪表板、演示文稿等

人们对 Excel 有很多误解。许多人认为它不过是处理临时预算的电子表格工具。或者它非常适合处理数据,但您需要像 PowerPoint 这样的单独工具才能很好地显示它。 这些误解限制了我们使用 Excel 的方式。 但 Excel 的功能远不止于此,它所需要的只是了解一些鲜为人知的功能。我…

JAVA SCRIPT设计模式--创建型设计模式之工厂方法(3)

JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能&#xff0c;所以不可能像C&#xff0c;JAVA等面向对象语言一样严谨&#xff0c;大部分程序都附上了JAVA SCRIPT代码&#xff0c;代码只是实现了设计模式的主体功能&#xff0c;不代…

机器学习笔记之配分函数(三)对比散度

机器学习笔记之配分函数——对比散度引言回顾&#xff1a;随机最大似然求解模型参数的过程随机最大似然的缺陷吉布斯采样的缺陷与对比散度思想对比散度名称的由来从KL\mathcal K\mathcal LKL散度的角度描述极大似然估计对比散度的本质引言 上一节介绍了随机最大似然(Stochasti…