ThreadLocal

本文深入解析了ThreadLocal的工作原理及其在Java中的应用。介绍了如何利用ThreadLocal实现线程安全,探讨了其在多线程环境中的优势及潜在的风险,如内存泄漏等问题。

ThreadLocal的核心是:每个线程都可以通过ThreadLocal对象的 get 或 set 方法来访问属于自己的,独立初始化的变量拷贝。

ThreadLocal 介绍

  • 我们想要在多线程当中拥有某个类的多个独立的私有化实例对象而不造成冲突。每个对象唯一对应一个线程。这其中(指ThreadLocal)便是实现了线程安全。

  • 有一点需要注意的是,ThreadLocal变量是全局可访问的。我们可以从线程中的任何一个地方来访问它。同时注意,TheadLocal 变量是被声明为static 和final的。

什么是线程安全?

一个线程相当于一个单一的流水线执行过程。当我们谈及多线程应用时,指的是多个流水线执行着相同的代码(感觉这里说得不太对,但原文就是:we mean that there multiple(sequential flow of control) line of process that runs through the same lines of code.)。这种情况下,就有可能出现某个线程访问或修改另一个线程的数据。如果我们不允许这种情况出现,就得使它能够线程安全。以下是几种实现线程安全的操作:
- 重入(Re-entrancy, java 里有个重入锁类: ReentrantLock , 实现的是可被中断的同步)
- 同步执行 Mutual exclusion(synchronization)
- Thread-local
- 原子操作 (Atomic operation)

根据上面说的可,ThreadLocal也是实现线程安全的一种选择,现在我们看看ThreadLocal如何实现。

ThreadLocal 的用法

这里我不禁引用 Joshua Bloch 的话(对于这一节我还有什么更好的人选),以下是他的原话(这几句原话都不太理解,估计翻译不对,都附上原话。。):

  • 单纯每个线程的上下文, 例如 用户id 或者 交易id(transaction id)。都能很好工作。在线程退出的时候能够轻松地进行资源释放和清除工作。不会产生内存泄漏。( Genuine per-thread context, such as user id or transaction id. Works great. Easy to clean up when the thread exits the scope. No leaks.)

  • 每个线程的性能(Per-thread instances for performance.)

  • 当你不注意控制好回调的时候,脏数据将产生(“Sleazing” values through callbacks that you don’t control:):有时候,你必须调用一个回调你的函数的库函数(sometimes you must call a library method that calls back into your package)。这个时候,由于库函数的缺点,你无法自己将自己需要的上下文信息传递给自己(you need some context that you were unable to pass to yourself)。这种罕见的情况,thread locals将会成为你的救命恩人。

以上几个要点,用我自己的话来讲就是:我们拥有一个非线程安全的对象,当我们想要安全地使用它。我们可以 通过将该对象包裹于 synchronized 代码块中来实现同步。另一个方法就是使用ThreadLocal,它通过为每个线程持有分开的独立的对象拷贝来使其线程安全。

ThreadLocal实例

package com.javapapers;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadLocalExample {
    private static final ThreadLocal formatter = new ThreadLocal() {

        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date) {
        return formatter.get().format(date);
    }
}

在上面的例子中, get() 方法是理解的关键。它返回了当前线程对于这个thread-local变量的一份拷贝。如果该变量对于当前线程没有值,则会先进行初始化为 initialValue 方法的返回值。

java文档中的例子

下面的类为每个线程生成唯一的本地识别码(即id)。一个线程在它第一次调用ThreadId.get()的时候便获得了它的id,并且在后续调用过程中保持不变。

import java.util.concurrent.atomic.AtomicInteger;

 public class ThreadId {
     // Atomic integer containing the next thread ID to be assigned
     private static final AtomicInteger nextId = new AtomicInteger(0);

     // Thread local variable containing each thread's ID
     private static final ThreadLocal threadId =
         new ThreadLocal() {
             @Override protected Integer initialValue() {
                 return nextId.getAndIncrement();
         }
     };

     // Returns the current thread's unique ID, assigning it if necessary
     public static int get() {
         return threadId.get();
     }
 }
ThreadLocal在Java API中的使用

在JDK1.7中有了一个新的类叫ThreadLocalRandom。它可以用于为并行的线程产生随机数。随机数种子对于每个线程都是唯一的。这真是个很酷的工具。

以下是该类中对于ThreadLocal的使用:

    private static final ThreadLocal localRandom =
        new ThreadLocal() {
            protected ThreadLocalRandom initialValue() {
                return new ThreadLocalRandom();
            }
    };
ThreadLocalRandom 的使用例子
package com.javapapers;

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomExample {

    public static void main(String args[]) throws InterruptedException {

        //tossing 3 coins
        for (int i = 0; i < 3; i++) {
            final Thread thread = new Thread() {

                public void run() {
                    System.out.print(Thread.currentThread().getName() + ":");

                    // generating 3 random numbers - random for every thread
                    for (int j = 0; j < 3; j++) {
                        final int random = ThreadLocalRandom.current().nextInt(
                                1, 3);
                        System.out.print(random + ",");
                    }
                    System.out.println();
                }
            };
            thread.start();
            thread.join();
        }
    }
}

ThreadLocal与内存泄漏

ThreadLocal本身并不坏,它是个很有用的工具API。当着但这完全取决于我们如何使用它。我们应该学着在恰当的情况下选择合适的工具。我们不能用炮弹来攻击蚊子, 却责怪炮弹本身。

网上弥漫着对于ThreadLocal的强烈抱怨,说它会导致内存泄漏。大部分情况下,不是这样的!

我又想引用 Joshua Bloch 的话:
“…thead local本身并没有错,它们并没有导致内存泄漏,也并不慢。它们比非thread-local的副本更加本地化(local, 或者翻译为私有吧)(例如,它们有更好的信息隐藏属性)。他们有可能被误用,当然,大多数其他的编程工具也是如此…”

原文:http://javapapers.com/core-java/threadlocal/

农业作物成熟度实例分割数据集 一、基础信息 • 数据集名称:农业作物成熟度实例分割数据集 • 图片数量: 训练集:563张图片 验证集:161张图片 测试集:80张图片 总计:804张图片 • 训练集:563张图片 • 验证集:161张图片 • 测试集:80张图片 • 总计:804张图片 • 分类类别: bfullyripened: b类作物完全成熟状态 bgreen: b类作物绿色未成熟状态 bhalfripened: b类作物半成熟状态 lfullyripened: l类作物完全成熟状态 lgreen: l类作物绿色未成熟状态 lhalfripened: l类作物半成熟状态 • bfullyripened: b类作物完全成熟状态 • bgreen: b类作物绿色未成熟状态 • bhalfripened: b类作物半成熟状态 • lfullyripened: l类作物完全成熟状态 • lgreen: l类作物绿色未成熟状态 • lhalfripened: l类作物半成熟状态 • 标注格式:YOLO格式,包含实例分割多边形点标注,适用于实例分割任务。 • 数据格式:图片来源于农业图像数据库,细节清晰,适用于模型训练。 二、适用场景 • 农业AI监测系统开发:数据集支持实例分割任务,帮助构建能够自动识别作物部分并分类成熟度的AI模型,辅助农民进行精准农业管理。 • 精准农业应用研发:集成至农业智能平台,提供实时作物状态识别功能,优化灌溉、施肥和收获时间。 • 学术研究与创新:支持农业科学与人工智能交叉领域的研究,助力发表高水平农业AI论文。 • 农业教育与培训:数据集可用于农业院校或培训机构,作为学生学习作物识别和成熟度评估的重要资源。 三、数据集优势 • 精准标注与多样性:每张图片均经过精确标注,确保实例分割边界准确,类别分类正确。涵盖两种作物类型(b和l)和三种成熟度状态(完全成熟、绿色未成熟、半成熟),具有高度多样性,提升模型泛化能力。 • 任务适配性强:标注兼容主流深度学习框架(如YOLO等),可直接加载使用,支持实例分割任务,并可扩展到其他计算机视觉任务。 • 农业价值突出:专注于作物成熟度检测,为智能农业、自动化收获和作物健康监测提供关键数据支持,具有重要的实际应用价值。
本资源包提供了针对STM32F103C8微控制器的MPU6050加速度计和陀螺仪的软件I2C驱动程序。通过集成数字运动处理(DMP)技术,能够实现高精度的姿态解算,适用于需要实时感知设备方向和动作的应用场景。此驱动还支持OLED显示屏,直接在设备上展示姿态信息,如俯仰、滚转和偏航角度,非常适合飞行器、机器人或是任何需要姿态监测的项目。 主要特点 软件I2C实现:无需专用的硬件I2C接口,适用于资源有限的STM32F103C8开发板。 DMP姿态解算:利用MPU6050内置的DMP,提供准确的姿态估计。 OLED显示集成:可以直接在OLED屏幕上显示加速度和角速度等姿态数据,便于实时监控。 兼容性说明:已验证与匿名地面站通信协议6.0版本兼容,特别适合与V6.56版本的地面站软件配合使用。 示例代码:包含完整的演示代码,方便用户快速上手并进行二次开发。 使用指南 环境配置:确保你的开发环境支持STM32F103C8,推荐使用STM32CubeIDE或Keil uVision等IDE。 库文件导入:将提供的源码文件夹导入到你的工程中。 配置I2C和OLED:根据你具体的硬件连接调整I2C引脚配置及OLED初始化设置。 连接MPU6050:按照电路图正确连接MPU6050传感器至STM32F103C8的I2C线路上。 编译与调试:编译项目,并通过ST-LINK或其他编程器将固件烧录到STM32F103C8。 测试与应用:运行程序,观察OLED屏幕上的姿态数据显示,可通过匿名地面站软件进行远程监控(需符合通信协议)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值