怎样实现两个线程共享一个集合

您所在的位置:网站首页 两个线程共用一个变量怎么办啊 怎样实现两个线程共享一个集合

怎样实现两个线程共享一个集合

2024-07-10 15:48| 来源: 网络整理| 查看: 265

一、概述

在Java多线程编程当中,对于被多个线程的共享变量,一般的方式是通过加锁,如使用synchronized关键字或者Java并发包的ReentrantLock加锁来实现线程安全,或者该变量在Java并发包存在线程安全的版本实现,如整数Integer对应的AtomicInteger,HashMap对应的ConcurrentHashMap等,则使用对应的线程安全版本的实现。除了以上两种方式之外,Java还提供了另外一种方式就是使用线程本地变量ThreadLocal来对共享变量进行包装。ThreadLocal是基于空间换时间的思路来设计的,即通过使用ThreadLocal对共享变量进行包装,使得每个线程都包含这个共享变量的一个副本,每个线程都对自己的共享变量副本进行操作,这样就实现了这个共享变量对每个线程的独立性,这样就不需要通过加锁来实现线程安全。不过由于每个线程都包含了这个共享变量的一个副本,所以会额外占用一定的内存空间,并且会随着线程数量的增加而增大,特别是如果这个共享变量会占用比较多空间,如用于存放数据的字典结构HashMap,则空间会增加更多。所以在选择是否使用ThreadLocal时,需要对该共享变量的空间占用进行一个衡量。

二、使用方法

通过ThreadLocal来对共享变量进行包装来实现线程安全通常用在对类的静态变量或者被共享的对象的内部属性。如下示例为通过ThreadLocal来对类的静态变量来进行包装,例子的含义是:静态变量nextWorkId用于生成每个线程的操作序号,即每个线程每进行一次操作,递增产生一个序号来标识这次操作是当前线程的第几次操作。 cb4af11b26ae3e9590a13764b218edb5.png 执行的打印如下:可以看到两个线程都是执行了10次,序号不会相互影响。 6d1cd3c2b4c9f2cd01359a97e3b48644.png

三、核心实现

在实现层面,首先是在Thread类中包含一个字典类型的成员变量threadLocals,用于存放该Thread线程对象所包含的所有使用ThreadLocal包装的变量的集合,其中这个字典类型是在ThreadLocal内部定义的一个静态内部类ThreadLocalMap,该字典实现的key是ThreadLocal对象引用,值为该ThreadLocal对象所包装的具体值。由于是每个Thread线程对象都包含这样一个字典集合,所以实现了每个线程都包含对应变量的一份副本。

Thread类的线程本地变量字典threadLocals

Thread类的threadLocals定义如下:可以看出类型是ThreadLocal.ThreadLocalMap。 ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal类定义

由以上分析可知,Thread类的线程本地变量字典threadLocals的类型ThreadLocalMap是在ThreadLocal中定义的,ThreadLocalMap的核心定义如下:可以看出与常用的字典结构HashMap类似,也是基于链式哈希表实现的。 c20de114cfc3418b2f3ff0f8718f28f6.png

1. ThreadLocal的值初始化

当使用ThreadLocal对某个变量进行包装时,一般首先需要对这个变量进行初始化,不过也可以通过调用set方法在之后使用时再设值。对ThreadLocal变量进行初始化主要是通过其initialValue方法来实现的,如下:默认实现为返回null,该方法是protected方法,故可以在创建ThreadLocal对象时,重写这个方法来自定义初始化逻辑。 protected T initialValue() { return null;}

重写initialValue方法来自定义初始化逻辑:如下初始化Integer类型的nextWorkId的值为1

38facdf687a410c47d229b47eeaa731b.png

2. ThreadLocal的get方法:获取线程绑定的值

初始化值或者调用set方法写值之后,则在使用时,一般会通过ThreadLocal的get方法来获取该ThreadLocal所包装的变量对应的值,由于每个线程都是获取到与该线程绑定的值,即从该Thread线程对象所关联的线程本地变量集合threadLocals中获取,所以在get方法的内部实现当中,首先需要获取当前调用这个get方法的线程的对象引用Thread,即通过调用Thread.currentThread()方法获取,然后使用当前的ThreadLocal对象引用作为key,从该Thread线程对象的成员变量threadLocals获取对应的值,具体实现如下: 1fe4924d98de953bae7f23bf379a205c.png

3. ThreadLocal的set方法:设置线程绑定的值

set方法主要是往Thread线程对象的threadLocals集合中设置该ThreadLocal对应的值,与get方法实现类似,也是先拿到当前调用这个set方法的线程的对象引用Thread,然后在往该Thread对象引用的threadLocals集合中设置值,其中key为当前的ThreadLocal对象引用,值为通过方法参数传递进来的实际的值,具体实现如下: 579db44a2cc6736481c79ce7bc0a4685.png


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3