ThreadLocal线程安全示例及其原理

这篇具有很好参考价值的文章主要介绍了ThreadLocal线程安全示例及其原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示:多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的

ThreadLocal是用空间换取时间,synchronized关键字是用时间换空间。


前言

线程安全是多线程编程时的计算机程序代码中的一个概念。 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。


提示:以下是本篇文章正文内容,下面案例可供参考

一、示例

以下示例说明了两个线程操作同一对象的过程中,线程安全和线程不安全的两种结果

ThreadLocal线程安全示例

package com.mabo;

import java.util.concurrent.TimeUnit;

public class TheadLocalTest {
    private static ThreadLocal<Integer> threadLocalStudent = new ThreadLocal<>();
    private static int a=0;
    static {
        threadLocalStudent.set(a);
    }
    public static void main(String[] args) {
        // 简单写一个测试线程隔离的例子
        // 原料: 1个ThreadLocal类型的变量 2个线程
        // 期望结果:线程一set的变量 线程二get不到!
        new Thread(()->{
            a=2;
            threadLocalStudent.set(a);
            System.out.println(Thread.currentThread()+"线程保存的对象:"+threadLocalStudent.get());
            try {
                // 细节!!! 先睡一会再get避免误差。
                // 可见这是一个严谨性很高的测试Demo
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"的值为"+threadLocalStudent.get());
        }).start();
        new Thread(()->{
            a=6;
            threadLocalStudent.set(a);
            System.out.println(Thread.currentThread()+"线程保存的对象:"+threadLocalStudent.get());
            try {
                // 细节!!! 先睡一会再get避免误差。
                // 可见这是一个严谨性很高的测试Demo
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"的值为"+threadLocalStudent.get());
        }).start();
    }
}

执行结果
ThreadLocal线程安全示例及其原理

非线程安全示例

package com.mabo;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

public class Test {
    private static int a=0;
    /**
     * @Description : 测试
     * 当一个线程操作的过程中被另一个线程操作了当前对象,线程不安全
     * @Author : mabo
    */

    public static void main(String[] args) {
        new Thread(()->{
            a=2;
            System.out.println(Thread.currentThread()+"线程保存的对象:"+a);
            try {
                // 细节!!! 先睡一会再get避免误差。
                // 可见这是一个严谨性很高的测试Demo
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"的值为"+a);

        }).start();
        new Thread(()->{
            a=6;
            System.out.println(Thread.currentThread()+"线程保存的对象:"+a);
            try {
                // 细节!!! 先睡一会再get避免误差。
                // 可见这是一个严谨性很高的测试Demo
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"的值为"+a);
        }).start();
    }
}

执行结果
ThreadLocal线程安全示例及其原理

二、ThreadLocal线程安全原理

线程安全原理

可以看到ThreadLocal操作值的时候是取得当前线程的ThreadLocalMap对象,然后把值设置到了这个对象中,这样对于不同的线程得到的就是不同的ThreadLocalMap,那么向其中保存值或者修改值都只是会影响到当前线程,这样就保证了线程安全。

源码如下

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

内存泄漏

由于Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是一样长,如果都没有手动删除对应key,都会导致内存泄漏。

但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set(),get(),remove()的时候会被清除。

因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

ThreadLocal正确的使用方法
每次使用完ThreadLocal都调用它的remove()方法清除数据
将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。

三、其他线程安全的集合

Vector(不推荐)

Vector和ArrayList类似,是长度可变的数组,与ArrayList不同的是,Vector是线程安全的,它几乎给所有的public方法都加上了sychronized关键字。由于加锁倒是性能降低,在不需要并发访问时,这种强制性的同步就显得多余,所以现在几乎没有什么人在使用。

HashTable

HashTable和HashMap类似,不同的是HashTable是线程安全的,它也是几乎的给所有的public方法都加上了sychronized关键字,还有一个不同点是:HashTable的K, V都不能是null,但是HashMap可以。

Collections包装方法

由于ArrayList和HashMap性能好,但是不是线程安全,所以Collections工具类中提供了相应的包装方法将他们包装成相应的线程安全的集合:

List<E> synchronizedList = Collections.sychronizedList(new ArrayList<E>());

Set<E> sychronizedSet = Collections.sychronizedSet(new HashSet<E>());

Map<K, V> synchronizedMap = Collections.sychronizedMap(new HashMap<K, V>());

Collections针对每种集合都声明了一个线程安全的包装类,在原集合的基础上添加了锁对象,集合中的每个方法都通过这个锁对象实现同步

ConcurrentHashMap(多个桶,锁部分)

ConcurrentHashMap和HashTable都是线程安全的集合,它们的不同主要是加锁粒度上的不同。HashTable的加锁方法是给每个方法加上synchronized关键字,这样锁的是整个对象。而ConcurrentHashMap是有更细粒度的锁。
在JDK1.8之前,ConcurrentHashMap加的是分段锁,即Segment,每个Segment含有整个table的一部分,这样不同分段之间的并发操作就互不影响。
JDK1.8之后对此进行了进一步的改进,取消了Segment,直接在table元素上加锁,实现对每一行加锁,进一步减小了并发冲突的概率。

CopyOnWriteArrayList和CopyOnWriteArraySet

针对涉及到数据修改的部分,都会使用ReentrantLock锁住操作,并将修改或添加的元素,通过拷贝的方式,加入数组中,最后修改数组的引用为新复制的数组。读取时直接读取数组,不需要获取锁。

四、Java并发编程12种锁的具体实现方式

点击如下链接查看
Java并发编程12种锁的具体实现方式–点击此处跳转

总结

ThreadLocal是用空间换取时间,synchronized关键字是用时间换空间。
如何使用,需要根据需要合理使用文章来源地址https://www.toymoban.com/news/detail-465235.html

到了这里,关于ThreadLocal线程安全示例及其原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 单例模式及其线程安全问题

    目录 ​ 1.设计模式 2.饿汉模式 3.懒汉模式 4.线程安全与单例模式 设计模式是什么? 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案 这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的 单例模式的作用就是保证某个类在程序

    2024年02月03日
    浏览(37)
  • Java多线程基础-8:单例模式及其线程安全问题

    单例模式是经典的设计模式之一。什么是设计模式?代码的设计模式类似于棋谱,棋谱就是一些下棋的固定套路,是前人总结出来的一些固定的打法。依照棋谱来下棋,不说能下得非常好,但至少是有迹可循,不会下得很糟糕。代码的设计模式也是一样。 设计模式,就是软件

    2024年02月05日
    浏览(50)
  • ThreadLocal 本地线程变量详解

    ThreadLocal 意为本地线程变量,即该变量只属于当前线程,对其他线程隔离 我们知道,一个普通变量如果被多线程访问会存在存在线程安全问题,这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问,从而解决并发安全问题 但如果这个变量并不需要被共享,

    2024年02月05日
    浏览(46)
  • 深入解析域名短链接生成原理及其在Python/Flask中的实现策略:一篇全面的指南与代码示例

    为了构建一个高效且用户友好的域名短链服务,我们可以将项目精简为以下核心功能板块: 1. 用户管理 注册与登录 :允许用户创建账户并登录系统。 这部分内容可以参考另一片文章实现: 快速实现用户认证:使用Python和Flask配合PyJWT生成与解密Token的教程及示例代码 资料管

    2024年02月22日
    浏览(54)
  • ThreadLocal:线程中的全局变量

    最近接了一个新需求,业务场景上需要在原有基础上新增2个字段,接口新增参数意味着很多类和方法的逻辑都需要改变,需要先判断是否属于该业务场景,再做对应的逻辑。原本的打算是在入口处新增变量,在操作数据的时候进行逻辑判断将变量进行存储或查询。 如果全链

    2024年02月10日
    浏览(41)
  • ThreadLocal加切面实现线程级别的方法缓存

    当一个请求线程多次请求A方法时,只会触发一次A方法的实际调用,会将方法结果缓存起来,避免多次调用。 1. 需要一个注解ThreadLocalCache,在需要缓存的方法上加上该注解 2. 需要一个切面,借助ThreadLocal,将结果缓存起来,利用环绕通知来实现方法拦截从缓存中返回方法执行结果 3.1、

    2024年04月10日
    浏览(40)
  • 【网络安全篇】php伪协议-漏洞及其原理

    🏆今日学习目标: 🍀学习php伪协议 ✅创作者:贤鱼 ⏰预计时间:35分钟 🎉个人主页:贤鱼的个人主页 🔥专栏系列:网络安全 🍁贤鱼的个人社区,欢迎你的加入 贤鱼摆烂团 🍁如果有需要可以查看下面文章 25分钟了解php?php基础 举个例子 include(文件名) ; 作用是如果文件

    2024年02月03日
    浏览(50)
  • 【Java 并发编程】Java 线程本地变量 ThreadLocal 详解

    先一起看一下 ThreadLocal 类的官方解释: 用大白话翻译过来,大体的意思是: ThreadLoal 提供给了 线程局部变量 。同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意: 因为每个 Thread 内有自己的实例副本,且 该副本只能由当前 Thread 使用 。

    2024年02月04日
    浏览(71)
  • 最安全的Hash算法-Bcrypt原理及示例

        在当今这个数字化的世界中,数据的安全性变得越来越重要。密码是保护个人和企业数据的关键,然而,如何安全地存储和验证密码却是一个持续的挑战。bcrypt是一种解决这个问题的优秀加密算法,它结合了密码哈希函数和加密算法,使得密码验证过程既安全又高效。

    2024年04月17日
    浏览(29)
  • 【网络安全】https与证书原理 | SSL Pinning及其绕过

    参考: https://segmentfault.com/a/1190000009002353?sort=newest https://zhuanlan.zhihu.com/p/353571366 https://juejin.cn/post/6863295544828444686 HTTPS=HTTP+TLS,其它的协议也类似,如FTPS=FTP+TLS 1) ClientHello Client 首先发送本地的 TLS 版本、支持的加密算法套件,并且生成一个随机数 R1 。 2)Server Hello Server 端确

    2024年02月05日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包