Java中的ThreadLocal功能演示

Wesley13
• 阅读 640

除了使用synchronized同步符号外,Java中的ThreadLocal是另一种实现线程安全的方法。在进行性能测试用例的编写过程中,比较简单的办法就是直接使用synchronized关键字,修饰对象方法以及。但是使用synchronized同步,这可能会影响应用程序的可伸缩性以及运行效率。但是如果要在多个线程之间共享对象又要保障线程安全,则除了synchronized之外没有特别适合测试的方法。

Java中的ThreadLocal是实现线程安全的另一种方法,它不满足同步要求,而是通过为每个线程提供Object的显式副本来消除共享。由于不再共享对象,因此不需要同步,它可以提高应用程序的可伸缩性和运行效率。

在本文中,会介绍有关ThreadLocal的基础知识点点,DemoThreadLocal的简单示例。

ThreadLocal简介

很多人几乎都没有用过ThreadLocal类,因为在测试中能用到的地方实在太少了,而且测试脚本的性能一般来讲都会很高,远超被测服务的处理能力,所以即使全部使用synchronized也不会有任何问题。

但是ThreadLocal有很多真正的使用场景,这就是为什么将其添加到标准Java平台库中的原因。尽管知道现在多线程编程测试中对于ThreadLocal应用并不多,但是我会在后期多进行一些实践,分享给各位。

以下是JavaThreadLocal类的一些众所周知的用法:

  • ThreadLocal非常适合实现每个线程单例类或每个线程上下文信息(例如事务 ID)。

  • 可以将任何非线程对象包装在 ThreadLocal中,并且将其使用变为线程安全的。 ThreadLocal的经典示例之一是共享 SimpleDateFormat。由于 SimpleDateFormat不是线程安全的,因此使用全局格式化程序可能无法正常工作,但是使用每个线程格式化程序当然可以工作。

  • ThreadLocal提供了另一种扩展 Thread的方法。如果要保留信息或将信息从一个方法调用传递到另一个方法,则可以使用 ThreadLocal进行传递。

  • 由于不需要修改任何方法,因此可以提供极大的灵活性。

没有两个线程可以看到彼此的ThreadLocal变量。J2EE应用程序服务器中有一个ThreadLocal的真实示例,该服务器使用Java ThreadLocal变量来跟踪事务和安全上下文。

为了避免过多的创建和共享全局实例时的切换成本,将诸如数据库连接之类的重对象作为ThreadLocal共享是很有意义的。

ThreadLocal演示Demo

`package com.fun.ztest.java;

import com.fun.frame.SourceCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * ThreadLocal演示测试类
 */
public class FunTester extends SourceCode {

    public static Logger logger = LoggerFactory.getLogger(FunTester.class);

    /**
     * 这个是重点,通过ThreadLocal类重建线程私有的对象
     */
    private static final ThreadLocal format = new ThreadLocal() {

        @Override
        protected Object initialValue() {
            Object funTester = new Object();
            logger.info("初始化对象,线程: {} 对象: {}", Thread.currentThread().getName(), funTester.hashCode());
            return funTester;
        }
    };

    public static void main(String args[]) throws IOException, InterruptedException {
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new Fun());
            t.start();
        }
    }

    /**
     * 获取对象
     *
     * @return
     */
    public static Object get() {
        return format.get();
    }

    static class Fun implements Runnable {

        @Override
        public void run() {
            logger.info("线程: {} 对象: {}", Thread.currentThread().getName(), FunTester.get().hashCode());
        }

    }

}

`

控制台输出

`INFO-> 当前用户:fv,IP:10.60.193.37,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> 初始化对象,线程: Thread-1 对象: 347384150
INFO-> 初始化对象,线程: Thread-2 对象: 142607688
INFO-> 线程: Thread-1 对象: 347384150
INFO-> 线程: Thread-2 对象: 142607688
INFO-> 初始化对象,线程: Thread-3 对象: 1008357237
INFO-> 初始化对象,线程: Thread-4 对象: 559951532
INFO-> 线程: Thread-3 对象: 1008357237
INFO-> 线程: Thread-4 对象: 559951532
INFO-> 初始化对象,线程: Thread-5 对象: 748958847
INFO-> 线程: Thread-5 对象: 748958847

Process finished with exit code 0

`

如果查看上述程序的输出,则会发现,当不同的线程调用ThreadLocal类的get()方法而不是调用其initialValue()方法时,该方法将为该线程创建Object的互斥实例对象。由于Object在线程之间不共享,并且实质上在创建它自己的线程安全对象或者方法的线程本地是完全线程安全的。

ThreadLocal类知识点

  • Java的 ThreadLocalJDK 1.2上引入,但后来在 JDK 1.4中进行了泛化,以在 ThreadLocal变量上引入类型安全性。

  • ThreadLocal通常与 Thread一起使用,由 Thread执行的所有代码都可以访问 ThreadLocal变量,但是两个线程看不到彼此的 ThreadLocal变量。

  • 每个线程都拥有 ThreadLocal变量的互斥副本,该副本在线程完成或死亡(正常情况下或由于任何异常)后才有进行垃圾回收,因为这些 ThreadLocal变量没有任何其他线程引用。

  • Java中的 ThreadLocal变量通常是类中的私有静态字段,并在 Thread中维护其状态。

不要误解ThreadLocalSynchronization的替代方法,它全部取决于你自己的程序设计。如果设计允许每个线程拥有自己的对象副本,则可以使用ThreadLocal

项目中使用

这里一个处理requestid的类,通过ThreadLocal使用,可以保证每个请求都拥有唯一的一个追踪标记。

`public class TraceKeyHolder {
    private static ThreadLocal threadLocal = new ThreadLocal();

    public TraceKeyHolder() {
    }

    public static String getTraceKey() {
        return (String)threadLocal.get();
    }

    public static void setTraceKey(String traceKey) {
        threadLocal.set(traceKey);
    }

    public static void clear() {
        threadLocal.remove();
    }
}

`


FunTester优秀讲师 | 腾讯云+社区权威认证非著名测试开发,文章记录学习和感悟,欢迎关注,求职招聘邮箱FunTester@88.com

Java中的ThreadLocal功能演示

点击阅读原文,查看公众号历史文章

本文分享自微信公众号 - FunTester(NuclearTester)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这