首页 > 其他分享 >ThreadLocal 、 实例变量、静态变量 以及 局部变量的区别

ThreadLocal 、 实例变量、静态变量 以及 局部变量的区别

时间:2024-04-19 14:34:24浏览次数:26  
标签:count 变量 Thread 局部变量 t2 t1 ThreadLocal 线程 threadTest

ThreadLocal 的作用

ThreadLocal 用于声明一个变量,这个变量在每个 线程 中都会创建一份实例,各个线程之间的数据不能共享,某个线程中的 ThreadLocal 变量与线程进行绑定,能够保证变量的线程安全。
使用示例一:

/**  
 * @author Erywim 2024/4/16  
 */public class Temp {  
    public static void main(String[] args) {  
        ThreadTest threadTest = new ThreadTest();  
        Thread t1 = new Thread(threadTest, "t1");  
		Thread t2 = new Thread(threadTest, "t2");  
        t1.start();  
        // 暂停1秒  
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
        t2.start();  
    }}
  
@Data  
class ThreadTest implements Runnable {  
    private ThreadLocal<Integer> local = new ThreadLocal<>().withInitial(() -> 0);  
    @Override  
    public void run() {  
        local.set(local.get() + 1);  
        System.out.println(Thread.currentThread().getName()+"    local.get() = " + local.get());  
    }  
}

其中 t1 线程和 t2 线程都会拥有一个专属于自己线程的 local 变量,其值为初始值 0 。所以最终输出的结果是

t1    local.get() = 1
t2    local.get() = 1

使用示例二:如果对于创建的线程类实例操作,将其 local 属性先修改为其他的值,然后再用该实例创建线程

/**  
 * @author Erywim 2024/4/16  
 */public class Temp {  
    public static void main(String[] args) {  
        ThreadTest threadTest = new ThreadTest();  
        Thread t1 = new Thread(threadTest, "t1");  
        t1.start();  
        // 暂停秒  
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  

		//即便是主线程进行了修改其中的 ThreadLocal 变量,子线程中的 ThreadLocal 也不会变  
        threadTest.getLocal().set(100);  
        System.out.println("threadTest.local.get() = " + threadTest.getLocal().get());

        //如果修改的是线程类的实例数据,则子线程中的数据也会跟着改变  
        System.out.println(Thread.currentThread().getName() + "  count = " + threadTest.getCount());  
        threadTest.setCount(1000);  
        System.out.println(Thread.currentThread().getName() + "  count = " + threadTest.getCount());  
  
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
        Thread t2 = new Thread(threadTest, "t2");  
        t2.start();  
    }
 }  
   
@Data  
class ThreadTest implements Runnable {  
    private ThreadLocal<Integer> local = new ThreadLocal<>().withInitial(() -> 0);  
    private int count = 0;  
    @Override  
    public void run() {  
        local.set(local.get() + 1);  
        System.out.println(Thread.currentThread().getName()+"    local.get() = " + local.get());  
  
        count++;  
        System.out.println(Thread.currentThread().getName() + "  count = " + count);  
    }
}

对比实例变量 count ,在主线程进行值的修改后,再作为线程进行启动,由于用的是同一个线程类的实例对象创建的两个线程,所以 t1 线程和 t2 线程中的实例对象是共享的,且与主线程中的线程类的实例对象也共享。所以最终的结果为

t1    local.get() = 1
t1  count = 1
main  threadTest.local.get() = 100
main  count = 1
main  count = 1000
t2    local.get() = 1
t2  count = 1001

即:主线程对于线程类实例中 ThreadLocal 属性的更改不会影响到子线程执行时的值,子线程如果是初次创建的话,则永远是从 initial 时指定的初始值开始计算的。

对比 实例变量静态变量局部变量

实例变量(成员变量)

实例变量 存在于 堆内存 中,随着实例对象的创建而创建。如果多个线程使用的是同一个实例对象创建的,那么这些创建出来的线程将共享线程实例对象中的实例变量。
示例代码:

/**  
 * @author Erywim 2024/4/16  
 */public class Temp {  
	    public static void main(String[] args) {
	    ThreadTest threadTest = new ThreadTest();  
	    //线程 t1 更改count的值  
	    Thread t1 = new Thread(threadTest, "t1");  
	    t1.start();  
	    // 暂停秒  
	    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
	    //当前count的值  
	    System.out.println(Thread.currentThread().getName() + "   threadTest.getCount() = " + threadTest.getCount());  
	    //main 线程更改count的值  
	    threadTest.setCount(2000);  
	    System.out.println(Thread.currentThread().getName() + "   threadTest.getCount() = " + threadTest.getCount());  
	  
	    //线程 t2 也更改count的值  
	    Thread t2 = new Thread(threadTest, "t2");  
	    t2.start();  
	}
}  
  
  
@Data  
class ThreadTest implements Runnable {  
    private ThreadLocal<Integer> local = new ThreadLocal<>().withInitial(() -> 0);  
    private int count = 0;  
    @Override  
    public void run() {
        count++;  
        System.out.println(Thread.currentThread().getName() + "  count = " + count);  
    }}

主线程和子线程共享同一个线程实例对象,所以 count 值他们都能进行修改,最终结果输出为:

t1  count = 1
main   threadTest.getCount() = 1
main   threadTest.getCount() = 2000
t2  count = 2001

静态变量(类变量)

静态变量 也称 类变量,存放在 方法区 ,所有由这个类的 模板 创建的 实例对象 都能共享这个变量的值,一个实例对象修改了 静态变量 则其他所有实例对象中的 静态变量 的值都会被修改,他们本质指的是同一个值。
示例代码:

/**  
 * @author Erywim 2024/4/16  
 */
public class Temp {  
    public static void main(String[] args) {
	    ThreadTest threadTest = new ThreadTest();  
	    ThreadTest threadTest2 = new ThreadTest();  
	    Thread t1 = new Thread(threadTest, "t1");  
	    Thread t2 = new Thread(threadTest2, "t2");  
	    t1.start();  
	    // 暂停秒  
	    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
	    System.out.println(Thread.currentThread().getName() + "   threadTest.getCount() = " + ThreadTest.count);  
	    ThreadTest.count = 2000;  
	  
	    t2.start();  
	    System.out.println(Thread.currentThread().getName() + "   threadTest.getCount() = " + ThreadTest.count);  
	}
}

@Data  
class ThreadTest implements Runnable {  
    private ThreadLocal<Integer> local = new ThreadLocal<>().withInitial(() -> 0);  
    public static int count = 0;  
    @Override  
    public void run() {  
        local.set(local.get() + 1);  
        System.out.println(Thread.currentThread().getName()+"    local.get() = " + local.get());  
//        int count = 0;  
        count++;  
        System.out.println(Thread.currentThread().getName() + "  count = " + count);  
    }}

虽然 t1t2 使用的是两个实例对象,但是其静态变量是共享的,所以最终展现出来的是 maint1t2 共同修改的结果:

t1  count = 1
main   threadTest.getCount() = 1
main   threadTest.getCount() = 2000
t2  count = 2001

局部变量

局部变量 存在于 栈区 ,是随着方法的被调用而产生,随着方法执行结束而释放的变量。每执行一次方法都会产生全新的局部变量,与之前执行的状态无关。

笔者认为这个类型的变量和 ThreadLocal 类似
示例代码:

/**  
 * @author Erywim 2024/4/16  
 */
public class Temp {  
    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();  
        Thread t1 = new Thread(threadTest, "t1");  
        t1.start();  
        // 暂停秒  
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }  
  
        Thread t2 = new Thread(threadTest, "t2");  
        t2.start();  
    }  
}  
@Data  
class ThreadTest implements Runnable {  
    @Override  
    public void run() {  
        int count = 0;  
        count++;  
        System.out.println(Thread.currentThread().getName() + "  count = " + count);  
    }}

局部变量只存在于方法内部,不进行共享,所以 t1 线程和 t2 线程中的 count 都为 1,结果为:

t1  count = 1
t2  count = 1

ThreadLocal 和 局部变量的区别

笔者认为:
在多线程简单的应用场景中,例如:某个线程的作用只是运行了一个方法,而其中用到的变量每次都是在方法启动时初始化,方法执行完就销毁,那么 ThreadLocal局部变量 的差别不大。

但是,ThreadLocal 最重要的一个特性是这个变量与线程进行绑定了,也就是说在使用线程池的情况下,这个线程在多次被重复使用的时候,其值是可以保留上一次执行的最终结果的,这个变量是线程独立的。

(tip:使用 InheritableThreadLocal 可以让子线程继承父线程的值)

新人第一次发文章,记录自己学习的经历,如果有不正确的地方还请指正,蟹蟹你的观看

标签:count,变量,Thread,局部变量,t2,t1,ThreadLocal,线程,threadTest
From: https://www.cnblogs.com/Erywim/p/18145828

相关文章

  • 使用bat切换java版本环境变量
    使用bat切换java版本环境变量需求有多个项目,每个项目依赖的java版本不同,需要切换java版本。或者想试用java新版本新特性,需要切换java版本。针对以上情况,Windows情况虽然修改一下环境变量JAVA_HOME即可,但也相对繁琐,一开始在找有没有类似nvm这种工具,但是找的过程看到一篇文章......
  • NVM安装和环境变量配置
    1.卸载node(没有安装的可以直接跳过)nvm是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs,解决node各种版本存在不兼容现象。但在安装之前需要先卸载之前的nodejs1)在控制面版或者应用列表中卸载nodejs2)不行就全局搜索然后删除相......
  • Effective Python:第6条 把数据结构直接拆分到多个变量里,不要专门通过下标访问
    使用拆分(unpacking),就可以把元组里面的元素分别赋给多个变量。优点:1,通过unpacking来赋值要比通过下标去访问元组内的元素更清晰,而且这种写法所需的代码量通常比较少。2,便于原地交换两个变量;tb=[1,2]tb[0],tb[1]=tb[1],tb[0]print(tb)3,for循环或者类似的结构(例如推......
  • JTCR-数据类型、变量和数组-01
    原始类型Java是强类型语言,在编译时会检查所有变量、表达式的类型是否兼容。Java为数据定义了8种原始类型(primitivetype),分为4组:整型:byte、short、int、long,表示整数。浮点数:float、double,表示小数。字符:char,表示字符集中的元素。Boolean:boolean,表示true/false值。......
  • 【构建】start.sh脚本中变量被maven profile构建过程替换
    启动脚本readonlyAPP_NAME="${project.artifactId}"#定义当前应用的名称readonlyJAR_VERSION="${project.version}"#打包的JAR版本EXE_JAR="$APP_NAME-$JAR_VERSION.jar"如何通过maven构建的时候将相应变量内容进行替换呢依赖插件maven-resources-plug......
  • dbt flags 变量简单说明
    通过flags可以使用dbtcli的一些参数,比较常用的是对于增量物化处理的场景参考使用{%ifflags.FULL_REFRESH%}droptable...{%else%}--no-op{%endif%}说明支持的参数都在flags中可以看看,一些dbtadapter的实现都会使用到此变量参考......
  • paper list(JS变量名恢复)
    PapersListAGeneralPath-BasedRepresentationforPredictingProgramProperties.[pdf]UriAlon,MeitalZilberstein,OmerLevy,EranYahav.PLDI,2018.CharacterizingtheNaturalLanguageDescriptionsinSoftwareLoggingStatements.[pdf][code]Pinji......
  • bat中检查系统版本并添加系统变量
    bat中检查系统版本并添加系统变量补丁包中升级nodejs​版本时,需要检查下操作系统版本,如果低于Windows8.1​需要添加系统变量NODE_SKIP_PLATFORM_CHECK​@echoofffor/f"tokens=1,2,3,4"%%iin('ver')do(setver_temp=%%l)setver_major=%ver_temp:~0,2%se......
  • java多线程 读取变量同步安全的案例
    本次介绍,我使用的是synchronized同步代码块的关键字来读取list,在写java多线程时,一定要注意synchronized关键字的有效范围。ps:如果synchronized关键字的代码块范围太大,可能会导致优先获取到cpu资源的第一个线程在满足条件的情况下一直无法跳循环,从而使得其他线程无法给获......
  • bat中检查系统版本并添加系统变量
    bat中检查系统版本并添加系统变量补丁包中升级nodejs​版本时,需要检查下操作系统版本,如果低于Windows8.1​需要添加系统变量NODE_SKIP_PLATFORM_CHECK​@echoofffor/f"tokens=1,2,3,4"%%iin('ver')do(setver_temp=%%l)setver_major=%ver_temp:~0,2%se......