本文共 2633 字,大约阅读时间需要 8 分钟。
本节书摘来异步社区《Java 7并发编程实战手册》一书中的第1章,第1.10节,作者:【西】Javier Fernández González,更多章节内容可以访问云栖社区“异步社区”公众号查看。
共享数据是并发程序最核心的问题之一,对于继承了Thread类或者实现了Runnable接口的对象来说尤其重要。
如果创建的对象是实现了Runnable接口的类的实例,用它作为传入参数创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性。也就是说,如果你在一个线程中改变了一个属性,所有线程都会被这个改变影响。
在某种情况下,这个对象的属性不需要被所有线程共享。Java并发API提供了一个干净的机制,即线程局部变量(Thread-Local Variable),其具有很好的性能。
本节中,我们将创建两个程序:第一个具有刚才提到的问题,另一个使用线程局部变量机制解决了这个问题。
准备工作
本节的范例是在Eclipse IDE里完成的。无论你使用Eclipse还是其他的IDE(比如NetBeans),都可以打开这个IDE并且创建一个新的Java工程。范例实现
按照接下来的步骤实现本节的范例1.使要实现的范例具有之前提到的共享问题。创建一个名为UnsafeTask的类,它实现了Runnable接口。声明一个私有的java.util.Date属性。
public class UnsafeTask implements Runnable{ private Date startDate;```2.实现run()方法,这个方法将初始化startDate属性,并且将值打印到控制台,让线程休眠一个随机时间,然后再次将startDate的值打印到控制台。
@Override
public void run() { startDate=new Date(); System.out.printf("Starting Thread: %s : %sn",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(); } } }}```4.在下面的截屏中,你将看到这个程序执行的结果。每个线程有一个不同的开始时间,但是当它们结束时,三个线程都有相同的startDate属性值。5.如之前提到的,我们将使用线程局部变量机制来解决这个问题。6.创建一个SafeTask类,用以实现Runnable接口public class SafeTask implements Runnable {7.声明一个ThreadLocal对象。这个对象是在initialValue()方法中隐式实现的。这个方法将返回当前日期。
private static ThreadLocal startDate= new
ThreadLocal() { protected Date initialValue(){ return new Date(); }};```
8.实现run()方法。它跟UnsafeTask类的run()方法实现了一样的功能,但是访问startDate属性的方式改变了。
@Overridepublic 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()); }```9.这个范例的入口类与第一个范例一样,只是创建并作为参数参入的Runnable类对象不同而已。10.运行范例,并分析两个范例之间的不同。工作原理在下面的截屏中,你将看到安全线程类的执行结果。现在,这3个线程对象都有它们自己的startDate属性值。线程局部变量分别为每个线程存储了各自的属性值,并提供给每个线程使用。你可以使用get()方法读取这个值,并用set()方法设置这个值。如果线程是第一次访问线程局部变量,线程局部变量可能还没有为它存储值,这个时候initialValue()方法就会被调用,并且返回当前的时间值。更多信息
转载地址:http://yauwa.baihongyu.com/