Java 7 Concurrency Cookbook 翻译 第一章 线程管理之五
九、使用线程本地变量
一个并发程序的最关键特征就是共享数据。这个特性在那些继承了 Thread 类或者 实现了 Runnable 接口的对象上显得更加重要。
如果你创建一个实现了 Runnable 接口的对象,然后再使用多个 Thread 对象去运行这个相同 Runnable 对象,这样所有的 Thread 对象就会共享相同的属性。这意味着你在其中一个 Thread 对象中对属性做的修改会影响其他的 Thread 对象。
有时,你需要运行同一个 Runnable 对象的多个 Thread 对象都有自己的独立的属性,而不是共享一个。Java的并发 API 中提供了一个高效且清晰的机制叫做线程本地变量来实现此特性。
在本秘诀中,我们首先开发一个程序来演示问题,然后使用线程本地变量的机制在另一个程序中来解决这个问题。
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) { SafeTask task = new SafeTask(); for (int i = 0; i < 3; i++) { Thread thread = new Thread(task); thread.start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } } 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()); } }
在没有使用线程本地变量之前,在程序执行结束后,所有线程的时间都相等,在使用了线程本地变量之后,线程的时间就不相等了。
线程本地变量针对每一个属性为每一个线程保存有自己的值。你可以使用 get() 和 set() 方法来分别获取和设置属性的值。线程本地变量的 initialValue() 方法会在该变量第一次被访问的时候执行,可以用来设置初始值。
注意:线程本地变量还有一个 remove() 方法可以方便地用来溢出线程线程本地变量。
Java并发API中的 inheritableThreadLocal 类提供了子线程对父线程线程本地变量的继承性。举例来说就是:线程A有一个本地变量的值是X,如果线程A产生了线程B,那么线程B也具有本地变量且值也是X。当然,你可以通过覆写该类的 childValue() 方法来定制行为。
十、聚集多个线程成线程组
Java并发API提高了一个有趣的功能:聚集线程成线程组。线程组使得我们可以以一个整体的形式来操控线程组内的所有线程。举例来说,你想控制一些执行相同任务的线程,这时把它们聚集成线程组,你对线程组使用一次调用就对它们每一个发出了相同的调用。
Java中线程组的对应类就是 ThreadGroup 类。ThreadGroup 对象可以从 Thread 对象或者 ThreadGroup 对象构造而来,相互组合可以形成树状结构。
在本秘诀中,我们使用 ThreadGroup 来达到如下场景:线程组内的10个线程执行耗时不同的任务,一旦一个执行完毕,终结组内所有线程。
public class Main { public static void main(String[] args) { ThreadGroup threadGroup = new ThreadGroup("Searcher"); Result result = new Result(); SearchTask searchTask = new SearchTask(result); for (int i = 0; i < 5; i++) { Thread thread = new Thread(threadGroup, searchTask); thread.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.printf("Number of Threads: %d\n", threadGroup.activeCount()); System.out.printf("Infomation about the Thread Group\n"); threadGroup.list(); Thread[] threads = new Thread[threadGroup.activeCount()]; threadGroup.enumerate(threads); for (int i = 0; i < threadGroup.activeCount(); i++) { System.out.printf("Thread %s: %s\n", threads[i].getName(), threads[i].getState()); } waitFinish(threadGroup); threadGroup.interrupt(); } private static void waitFinish(ThreadGroup threadGroup) { while (threadGroup.activeCount() > 4) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Result { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class SearchTask implements Runnable { private Result result; public SearchTask(Result result) { this.result = result; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.printf("Thread %s: Start\n", name); try { doTask(); result.setName(name); } catch (InterruptedException e) { System.out.printf("Thread %s: Interrupted\n", name); return; } System.out.printf("Thread %s: End\n", name); } private void doTask() throws InterruptedException { Random random = new Random((new Date()).getTime()); int value = (int) (random.nextDouble()*100); System.out.printf("Thread %s: %d\n", Thread.currentThread().getName(), value); TimeUnit.SECONDS.sleep(value); } }
ThreadGroup 类存储了与其相关联的 Thread 对象和 ThreadGroup 对象,所以其可以很方便地访问它们的状态和控制它们。
重要:本系列翻译文档也会在本人的微信公众号(此山是我开)第一时间发布,欢迎大家关注。
Java 7 Concurrency Cookbook 翻译 第一章 线程管理之五的更多相关文章
- Java 7 Concurrency Cookbook 翻译 第一章 线程管理之一
一.简介 在计算机的世界里,当我们谈论并发时,我们指的是一系列的任务同时运行于一个计算机中.这里说的同时运行,在计算机拥有多于一个处理器或者是一个多核处理器的时候才是真正的同时,在计算机只拥有单核处理 ...
- Java 7 Concurrency Cookbook 翻译 第一章 线程管理之六
十一.处理线程组中的未控制异常 每种编程语言一个很重要的特性就是其所提供的用来处理程序中错误情况的机制.Java语言和其他的现代语言一样,是提供了异常机制来处理对象程序中的错误.Java提供了很多的类 ...
- Java 7 Concurrency Cookbook 翻译 第一章 线程管理之四
七.创建和运行一个后台线程 Java中有一种特别的线程叫做 deamon(后台) 线程.这类线程具有非常低的权限,并且只有在同一个程序中没有其他的正常线程在运行时才会运行.注意:当一个程序中只剩下后台 ...
- Java 7 Concurrency Cookbook 翻译 第一章 线程管理之二
三.中断一个线程 一个拥有多个线程的Java程序要结束,需要满足两个条件之一:一是所有的非后台线程都执行结束了:二是某个线程执行了 System.exit() 方法.当你想要终结一个运行中的Java程 ...
- Java 7 Concurrency Cookbook 翻译 第一章 线程管理之三
五.睡眠和唤醒一个线程 有时,你会想要在一段特定的时间后再去中断线程的运行.举个例子,程序中的一个线程每一分钟检查一次传感器的状态,剩余的时间,线程应该处于空闲的状态.在这段空闲时间里,线程不会使用计 ...
- Java 7 Concurrency Cookbook 翻译 序言
在日常的Java代码开发过程中,很难免地有对多线程的需求,掌握java多线程和并发的机制也是Java程序员写出更健壮和高效代码的基础.笔者找寻国内已出版的关于Java多线程和并发的的中文书籍和翻译书籍 ...
- java的优点和误解 《java核心技术卷i》第一章
<java核心技术卷i>第一章主要内容包括三点: 1:Java白皮书的关键术语:描述Java的十一个关键字: 2:Java applet 3 :关于Java的常见误解 1:第一章:Ja ...
- java JDK8 学习笔记——第11章 线程和并行API
第11章 线程与并行API 11.1 线程 11.1.1 线程 在java中,如果想在main()以外独立设计流程,可以撰写类操作java.lang.Runnable接口,流程的进入点是操作在run( ...
- Java 螺纹第三版 第一章Thread介绍、 第二章Thread创建和管理学习笔记
第一章 Thread导论 为何要用Thread ? 非堵塞I/O I/O多路技术 轮询(polling) 信号 警告(Alarm)和定时器(Timer) 独立的任务(Ta ...
随机推荐
- SQL总结二
一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...
- echosp 销量排行 新增实际价格
找到lib_goods.php第147行,代码 $sql = 'SELECT g.goods_id, g.goods_name, g.shop_price,g.goods_thumb, SUM(og. ...
- SDK
IOS: iOS Application Life Cycle 应用程序生命周期 http://www.cnblogs.com/chenyg32/p/3873301.html iOS应用程序生命周期( ...
- [git]添加项目到git
写在前面 一直在想把代码托管到git上面,一直没有去研究,最近发现自己写的demo,好多都找不到了,实在是没办法了,耐下心研究了下git.这里通过添加了自己做的demo,算是也是学习下git的操作吧. ...
- linq/EF/lambda 比较字符串日期时间大小
在使用EF时,想要比较字符串类型的日期时,参考以下: SQL语句: 1 2 3 4 1)select * from TableName where StartTime > '2015-04-08 ...
- 数字格式化函数:Highcharts.numberFormat()
(转)数字格式化函数:Highcharts.numberFormat() 一.函数说明 该函数用于图表中数值的格式化,常见用途有数值精度控制.小数点符.千位符显示控制等. 二.函数使用 1.函 ...
- \r,\n,\r\n的区别
http://www.studyofnet.com/news/285.html \n是换行,英文是New line,表示使光标到行首\r是回车,英文是Carriage return,表示使光标下移一格 ...
- gtest
一.安装配置 1.简介 2.安装 下载地址: https://code.google.com/p/googletest/downloads/list 解压安装: unzip gtest-1.7.0.z ...
- [设计模式] javascript 之 迭代子模式
迭代子模式:定义 迭代子模式,又称游标模式,是一种用于对聚集进行顺序访问规则的模式,是一种行为模式:它用于提供对聚集对象的一种统一的访问接口,使客户能够在不了解聚集对象内部结构的情况对聚集对象进行访问 ...
- R-数据导入
目录 键盘输入 导入文本文件 导入Excel文件 访问数据库管理系统 键盘输入 > mydata <- data.frame(age=numeric(0), gender=characte ...