文档章节

线程局部变量的使用

柳哥
 柳哥
发布于 2015/04/26 13:02
字数 758
阅读 548
收藏 9

共享数据是并发程序最核心的问题之一,对于继承了Thread类或者实现了Runnable接口的对象来说尤其重要。如果创建的对象是实现了Runnable接口的类的实例,用它作为传入参数创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。也就是说,如果你在一个线程中改变了一个属性,所有线程都会被这个改变影响。

在某种情况下,这个对象的属性不需要被所有线程共享。Java并发API提供了一个干净的机制,即线程局部变量(Thread-Local Variable),其具有很好的性能。

这里我们将创建两个程序:第一个具有刚才提到的问题。另一个使用线程局部变量机制解决了这个问题。

第一个示例,显示共享才生的问题:

package concurrency;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Main3 {
    public static void main(String[] args) {
        UnsafeTask task = new UnsafeTask();
        for(int i=0; i<10;i++){
            Thread thread = new Thread(task);
            thread.start();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class UnsafeTask implements Runnable{
    private Date startDate;
    @Override
    public void run() {
        startDate = new Date();
        System.out.printf("Starting Thread:  %s  :  %s\n", Thread.currentThread().getId(),startDate);
        try {
            TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Thread Finished:  %s  :  %s\n", Thread.currentThread().getId(),startDate);
    }
}

第二个示例,例用线程局部变量解决第一个示例中的问题:

package concurrency;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Main4 {
    public static void main(String[] args) {
        SafeTask task = new SafeTask();
        for(int i=0; i<10;i++){
            Thread thread = new Thread(task);
            thread.start();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class  SafeTask implements Runnable{
    private static ThreadLocal<Date> startDate = new ThreadLocal<Date>(){
        protected Date initialValue(){
            return new Date();
        }
    };
    @Override
    public void run() {
        System.out.printf("Starting Thread:  %s  :  %s\n", Thread.currentThread().getId(),startDate.get());
        try {
            TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Thread Finished:  %s  :  %s\n", Thread.currentThread().getId(),startDate.get());
    }
}

声明一个ThreadLocal<Date>对象。Date的对象是在initialValue()方法中隐式实现的,这个方法将返回当前日期。线程局部变量分别为每个线程存储了各自的属性值,并提供给每个线程使用。你可以使用get()方法读取这个值,并用set()方法设置这个值。如果线程是第一次访问线程局部变量,线程局部变量可能还没有为它存储值,这个时候initialValue()方法就会被调用,并且返回当前的时间值。

线程局部变量也提供了remove()方法,用来为访问这个变量的线程删除已经存储的值。Java并发API包含了InheritableThreadLocal类,如果一个线程是从其他某个线程中创建的,这个类将提供继承的值。如果一个线程A在线程局部变量已有值,当它创建其他某个线程B时,线程B的线程局部变量将跟线程A是一样的。你可以覆盖childValue()方法,这个方法用来初始化子线程在线程局部变量中的值,它使用父线程在线程局部变量中的值作为传入参数。

© 著作权归作者所有

共有 人打赏支持
柳哥
粉丝 205
博文 405
码字总数 347782
作品 0
杭州
技术主管
私信 提问

暂无文章

0008-如何卸载CDH(附一键卸载github源码)

1.前置条件 本文档将介绍Cloudera Manager与CDH的卸载,并基于CDH使用parcels安装且未配置安全(AD/LDAP, Kerberos, Data Encryption)的集群,以下是本次测试环境,但不是本操作手册的硬限制...

Hadoop实操
15分钟前
1
0
MySQL Windows下免安装配置

安装MySQL软件 下载MySQL 依据ipems_dvp产品选型需求,选择mysql-5.7.17-winx64。可自行从网上下载,也可从公司拷贝,以下为下载目录和公司归档目录。 下载地址:https://downloads.mysql.c...

PeakFang-BOK
23分钟前
1
0
遍历java项目后台线程

遍历java项目后台线程,加深对项目后台运行线程理解 直接贴代码 import org.springframework.stereotype.Service;import java.lang.management.ManagementFactory;import java.lang.man...

王俊博客
37分钟前
2
0
iOS切面编程

aop编程(面向切面编程),其原理也就是在不更改正常的业务处理流程的前提下,通过生成一个动态代理类,从而实现对目标对象嵌入附加的操作。在iOS中,要想实现相似的效果也很简单,利用OC的动态性,...

RainOrz
44分钟前
2
0
PAT(Basic Level) 乙级练习题 ------ 1047 编程团体赛 java

1047.编程团体赛 题目: 编程团体赛的规则为:每个参赛队由若干队员组成;所有队员独立比赛;参赛队的成绩为所有队员的成绩和;成绩最高的队获胜。 现给定所有队员的比赛成绩,请你编写程序找...

Carol998
57分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部