当前位置:首页 > 开发 > 编程语言 > 多线程 > 正文

博弈Java讲义 - ThreadLocal

发表于: 2014-11-25   作者:boyitech   来源:转载   浏览次数:
摘要:   还记得Java并发最佳实践有一条提到尽量不要在线程间共享状态。但我们在实现一个thread或者runnable接口的时候很容易放这个错误,导致一些诡异的问题。   让我们看下面这个例子:   public class UnsafeTask implements Runnable { private Date startDate

  还记得Java并发最佳实践有一条提到尽量不要在线程间共享状态。但我们在实现一个thread或者runnable接口的时候很容易放这个错误,导致一些诡异的问题。 
  让我们看下面这个例子:

 

public 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);  
    }  
  
}  
  
public class Core {  
    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();  
            }  
        }  
    }  
}  

 

   我们看到如下输出: 

  

  

Starting Thread: 9 : Thu Feb 27 17:26:34 CST 2014  
Starting Thread: 10 : Thu Feb 27 17:26:36 CST 2014  
Starting Thread: 11 : Thu Feb 27 17:26:38 CST 2014  
Starting Thread: 12 : Thu Feb 27 17:26:40 CST 2014  
Thread Finished: 11 : Thu Feb 27 17:26:40 CST 2014  

   结束的线程显示的日期与刚启动的线程的日期是一样的,原因处在我们在同一个Runnable实例上启动了多个线程,而startDate域是多个线程之间共享的。 
  怎样避免这个问题呢? 一种方法是让一个线程对应一个Runnable实例,还有一种更有效的方法就是用Java Concurrency API提供的ThreadLocal变量,这种方法可以避免创建过多的Runnable实例。 
  看如下代码:

  

import java.util.Date;  
import java.util.concurrent.TimeUnit;  
  
public 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实现中有一个TheadLocalMap(开地址哈希?),存放各个Thread和值对应的值域,map的key是用线程和它的域的组合算出来的,这样每个线程就不共享状态了。 
  初次之外,还可以调用ThreadLocal的get(),set()方法去获得,更新自己的状态。 
  JDK还提供了一个更复杂的InheritableThreadLocal类,如果A线程创建了B线程,给类可以帮助B从A中获取相关状态域的一份拷贝。

 

 

博弈Java讲义 - ThreadLocal

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
自定义指令 本章我们将详细讲解如何用AngularJS实现自定义指令(directives)来扩充HTML. 指令的基本
本节我们将通过几个具体的例子来讲解Angular表单。 简单的表单 ngModel指令实现了双向的数据绑定,
动画 AngularJS 1.3版本为常用的指令(ngRepeat,ngSwitch,ngView...)提供了动画支持,另外还可以通
Bootstrap 本节我们将分析Angular应用初始化流程以及怎样手动启动Angular应用。 Angular<script&
什么是AngularJS ? AngularJS是一款非常优秀的前端MVVM(Model-View-ViewModel)框架,它强大的双向
Angular HTML编译器可以让开发者通过定制标签或者属性来拓展HTML语法。通过Angular HTML编译器,我
基本概念 在使用AngularJS之前,我们有必要梳理一下AngularJS核心概念及实现原理,以便于写出结构良
在AngularJS中数据绑定(Data-binding)意指模型和视图组件之间的数据自动同步。AngularJS应用是基于
在写Angular应用时,我们通常把与视图逻辑无关的公共代码片段写在service组件中,然后以依赖注入的
什么是作用域? Angular中作用域(scope)是模板以及工作的上下文环境,作用域中存放了应用模型和视图
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号