ThreadLocal 学习

ThreadLocal详解
本文详细介绍了ThreadLocal类的作用及其实现原理。通过实例演示了如何利用ThreadLocal为每个线程提供独立的变量副本,解决了线程间变量冲突的问题。

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

ThreadLocal的接口方法 

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

void set(T value)
设置当前线程的线程局部变量的值。
public T get()
该方法返回当前线程所对应的线程局部变量。

public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。

protected T initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。


Thread线程中成员变量是基本数据类型

public class IntegerThread extends Thread {
    //
    private int number;

    //
    @Override
    public void run() {
        super.run();
        for(int i=0;i<3;i++){
            System.out.println(getName()+"=>"+ ++number);
        }
    }
}

class Test {

    public static void main(String[] args) {
        new IntegerThread().start();
        new IntegerThread().start();
        new IntegerThread().start();
    }
}

输出:
Thread-0=>0
Thread-0=>1
Thread-0=>2
Thread-1=>0
Thread-1=>1
Thread-1=>2
Thread-2=>0
Thread-2=>1
Thread-2=>2
我们看到输出结果,每个线程都是自己是变量,没有任何问题,对吧。下面看一下多个线程共享同一个线程的引用变量 
public interface Sequence {
    public int getSequence();
}

public class SequenceNumber implements Sequence {
    //
    private int number;

    //
    @Override
    public int getSequence() {
        return ++number;
    }
}

public class SequenceThread extends Thread {
    //
    private Sequence sequecne;

    //
    public SequenceThread(Sequence sequence){
        this.sequecne = sequence;
    }

    //
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println(getName()+"=>"+sequecne.getSequence());
        }
    }
}

class Test {

    public static void main(String[] args) {
        Sequence sequence = new SequenceNumber();
        new SequenceThread(sequence).start();
        new SequenceThread(sequence).start();
        new SequenceThread(sequence).start();
    }
}

输出:
Thread-0=>1
Thread-0=>2
Thread-0=>3
Thread-1=>4
Thread-1=>5
Thread-1=>6
Thread-2=>7
Thread-2=>8
Thread-2=>9

从上面我们可以看出,所有的线程共享同一个Sequence,那么这里是有问题的。下面看一下我使用了ThreadLocal后会发生什么。

public interface Sequence {
    public int getSequence();
}

public class SequenceNumber implements Sequence {
    //
    private ThreadLocal<Integer> number = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    //
    @Override
    public int getSequence() {
        number.set(number.get()+1);
        return number.get();
    }
}


public class SequenceThread extends Thread {
    //
    private Sequence sequecne;

    //
    public SequenceThread(Sequence sequence){
        this.sequecne = sequence;
    }

    //
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println(getName()+"=>"+sequecne.getSequence());
        }
    }
}

class Test {

    public static void main(String[] args) {
        Sequence sequence = new SequenceNumber();
        new SequenceThread(sequence).start();
        new SequenceThread(sequence).start();
        new SequenceThread(sequence).start();
    }
}
输出:
Thread-0=>1
Thread-0=>2
Thread-0=>3
Thread-1=>1
Thread-1=>2
Thread-1=>3
Thread-2=>1
Thread-2=>2
Thread-2=>3

你看,有点神奇吧,number这样的成员引用类型的变量,又成了线程局部变量了。那么,ThreadLocal是如何做到的嘞?我听说是用一个map做到的,那么我们自己也写一个ThreadLocal,看一下效果吧。

public class MyThreadLocal<T> {
    //
    private Map<Thread,T> map = Collections.synchronizedMap(new HashMap<Thread, T>());


    //
    protected T initialValue(){
        return null;
    }

    //
    public T get(){
        Thread thread = Thread.currentThread();
        T value = map.get(thread);
        if(value==null && !map.containsKey(thread)){
            value = initialValue();
            map.put(thread,value);
        }
        return  value;
    }

    //
    public void set(T value){
        map.put(Thread.currentThread(),value);
    }

    //
    public void remove(){
        map.remove(Thread.currentThread());
    }
}


public class SequenceNumber implements Sequence {
    //
    private MyThreadLocal<Integer> number = new MyThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    //
    @Override
    public int getSequence() {
        number.set(number.get()+1);
        return number.get();
    }
}


class Test {

    public static void main(String[] args) {
        new SequenceThread().start();
        new SequenceThread().start();
        new SequenceThread().start();

    }
}
输出:
Thread-0=>1
Thread-0=>2
Thread-0=>3
Thread-1=>1
Thread-1=>2
Thread-1=>3
Thread-2=>1
Thread-2=>2
Thread-2=>3

我们看到,自己写的这个MyThreadLocal也可以达到相同的效果。这说明,MyThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。通过以上的代码,我们应该可以看出,ThreadLocal没有什么难的,只要花一点儿心想看一下,写一写,就可以掌握。








基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制方法。通过结合数据驱动技术与Koopman算子理论,将非线性系统动态近似为高维线性系统,进而利用递归神经网络(RNN)建模并实现系统行为的精确预测。文中详细阐述了模型构建流程、线性化策略及在预测控制中的集成应用,并提供了完整的Matlab代码实现,便于科研人员复现实验、优化算法并拓展至其他精密控制系统。该方法有效提升了纳米级定位系统的控制精度与动态响应性能。; 适合人群:具备自动控制、机器学习或信号处理背景,熟悉Matlab编程,从事精密仪器控制、智能制造或先进控制算法研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①实现非线性动态系统的数据驱动线性化建模;②提升纳米定位平台的轨迹跟踪与预测控制性能;③为高精度控制系统提供可复现的Koopman-RNN融合解决方案; 阅读建议:建议结合Matlab代码逐段理解算法实现细节,重点关注Koopman观测矩阵构造、RNN训练流程与模型预测控制器(MPC)的集成方式,鼓励在实际硬件平台上验证并调整参数以适应具体应用场景。
提供了一套完整的基于51单片机的DDS(直接数字频率合成)信号波形发生器设计方案,适合电子爱好者、学生以及嵌入式开发人员学习和实践。该方案详细展示了如何利用51单片机(以AT89C52为例)结合AD9833 DDS芯片来生成正弦波、锯齿波、三角波等多种波形,并且支持通过LCD12864显示屏直观展示波形参数或状态。 内容概述 源码:包含完整的C语言编程代码,适用于51系列单片机,实现了DDS信号的生成逻辑。 仿真:提供了Proteus仿真文件,允许用户在软件环境中测试整个系统,无需硬件即可预览波形生成效果。 原理图:详细的电路原理图,指导用户如何连接单片机、DDS芯片及其他外围电路。 PCB设计:为高级用户准备,包含了PCB布局设计文件,便于制作电路板。 设计报告:详尽的设计文档,解释了项目背景、设计方案、电路设计思路、软硬件协同工作原理及测试结果分析。 主要特点 用户交互:通过按键控制波形类型和参数,增加了项目的互动性和实用性。 显示界面:LCD12864显示屏用于显示当前生成的波形类型和相关参数,提升了项目的可视化度。 教育价值:本资源非常适合教学和自学,覆盖了DDS技术基础、单片机编程和硬件设计多个方面。 使用指南 阅读设计报告:首先了解设计的整体框架和技术细节。 环境搭建:确保拥有支持51单片机的编译环境,如Keil MDK。 加载仿真:在Proteus中打开仿真文件,观察并理解系统的工作流程。 编译与烧录:将源码编译无误后,烧录至51单片机。 硬件组装:根据原理图和PCB设计制造或装配硬件。 请注意,本资源遵守CC 4.0 BY-SA版权协议,使用时请保留原作者信息及链接,尊重原创劳动成果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值