文档章节

Java并发编程初级篇(八):ThreadLocal

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/23 16:30
字数 670
阅读 53
收藏 1

 

在Java中,如果使用了Runnable接口的实现类。用这个实现类的一个实例对象来启动多个线程,那么这个类中的变量就会被多个线程共用,不管你住没注意,这在“Java并发编程(一):线程实现与运行的两种方式”中已经出现过了。

我们现在基于这个例子来改造一下,看如何用一个Runnable的实例对象创建多个线程类,并且让每一个线程都有自己的私有属性。

我们用Java API提供的ThreadLocal类来替换int变量,ThreadLocal类作为Java API提供的一个线程局部变量,它是和线程绑定的,每一个线程都会有一个独立的副本。

通过查看ThreadLocal类的原代码,我们可以发现每一个线程内部都会有一个ThreadLocalMap属性,这个类的内部实现是一个Entry[]数组,(key=ThreadLocal,value=ThreadLocal对象的绑定值)。当你调用ThreadLocal.get()方法的时候,它就会获取当前线程对象的ThreadLocalMap属性中以ThreadLocal对象为健对应的值。如果不存在就会调用setInitialValue()方法,然后将initialValue()方法返回的值设置到ThreadLocalMapEntry[]数组中,并返回这个值。

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

下面看我们的实现代码,为线程定义一个ThreadLocal类型属性,具体实现ThreadLocal中的initialValue()方法来为这个ThreadLocal对象属性赋一个初始值。然后使用MyRunnable的一个实例启动三个线程。

public class MyRunnable implements Runnable {
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 3;
        }
    };

    @Override
    public void run() {
        while (num.get() > 0) {
            System.out.println("Thread:" + Thread.currentThread().getName() + ", consume " + num.get());
            num.set(num.get() - 1);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

查看控制台日志,你会发现每一个线程都使用了一个独立的num值。

Thread:Thread-0, consume 3
Thread:Thread-2, consume 3
Thread:Thread-1, consume 3
Thread:Thread-0, consume 2
Thread:Thread-1, consume 2
Thread:Thread-2, consume 2
Thread:Thread-2, consume 1
Thread:Thread-1, consume 1
Thread:Thread-0, consume 1

 

© 著作权归作者所有

共有 人打赏支持
阿拉德大陆的魔法师
粉丝 25
博文 91
码字总数 83019
作品 0
西城
程序员
私信 提问
干货系列1:Java互联网网站开发工程师 的技术提高与晋升路线(技术专精)

前几天写了自己对于Java软件开发工程师职业发展规划方面的一些感悟,陆续收到一些反馈,希望我能再就Java工程师不同的开发(职责)方向谈谈职业发展问题。(上一篇:Java软件开发工程师的自我...

半饱即好
06/26
0
0
Java面试:投行的15个多线程和并发面试题

本文由ImportNew -一杯哈希不加盐 翻译自dzone。欢迎加入翻译小组。转载请见文末要求。 多线程和并发问题已成为各种 Java 面试中必不可少的一部分。如果你准备参加投行的 Java 开发岗位面试,...

ImportNew
08/23
0
0
安卓开发必备知识体系:Java篇

大家好我是张拭心,自从各位朋友帮点广X开始,我发现我每天更有奔头了,走起路来也更有劲了,说啥也得更新的勤快一点。不过放心,我一定推送有价值的内容给大家,还请朋友们照旧动动手指点点...

d29h1jqy3akvx
05/10
0
0
如果你想学习Java,那么就来看这篇文章

一、前言 我是从大二开始学习的Java,当时的目标是Java Web开发,当时并不想考研,所以当时的学习是以就业为主,现在我大三了,学习Java Web开发已经一年了,因为种种原因,决定要考研,所以...

Jivanmoon
08/27
0
0
那些年,关于 Java 的那些事儿

版权声明:Follow your heart and intuition. https://blog.csdn.net/qq_35246620/article/details/78695893 温馨提示:本系列博文(含示例代码)已经同步到 GitHub,地址为「java-skills」,...

维C果糖
2017/12/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

JAVA设计模式之模板方法模式和建造者模式

一、前期回顾 上一篇《Java 设计模式之工厂方法模式与抽象工厂模式》介绍了三种工厂模式,分别是工厂方法模式,简单工厂方法模式,抽象工厂模式,文中详细根据实际场景介绍了三种模式的定义,...

木木匠
23分钟前
2
0
C中的宏的使用(宏嵌套/宏展开/可变参数宏)

基本原则: 在展开当前宏函数时,如果形参有#或##则不进行宏参数的展开,否则先展开宏参数,再展开当前宏。 #是在定义两边加上双引号 #define _TOSTR(s) #sprintf(_TOSTR(test ABC))pr...

SamXIAO
54分钟前
2
0
SpringBoot 整合异步调用方法

1. 在 SpringBoot 主类上使用 @EnableAsync 注解,开启异步调用功能 package com.codingos.springbootdemo;import org.springframework.boot.SpringApplication;import org.springfra......

北漂的我
今天
1
0
0015-如何使用Sentry管理Hive外部表权限

1.文档编写目的 本文档主要讲述如何使用Sentry对Hive外部表权限管理,并基于以下假设: 1.操作系统版本:RedHat6.5 2.CM版本:CM 5.11.1 3.集群已启用Kerberos和Sentry 4.采用具有sudo权限的...

Hadoop实操
今天
3
0
边缘计算与数据中心的发展趋势

导读 Gartner研究表明,人工智能、物联网和5G助力下一代商业创新,由此产生大量数据,2020年前企业将使用超过75亿台联网设备。 在几乎每个方面,社会的节奏都正变得更快。我们希望客户服务问...

问题终结者
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部