<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    线程管理(九)使用本地线程变量

    声明:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:方腾飞

    使用本地线程变量

    并发应用的一个关键地方就是共享数据。这个对那些扩展Thread类或者实现Runnable接口的对象特别重要。

    如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性。这意味着,如果你在一个线程里改变一个属性,全部的线程都会受到这个改变的影响。

    有时,你希望程序里的各个线程的属性不会被共享。 Java 并发 API提供了一个很清楚的机制叫本地线程变量。

    在这个指南中, 我们将开发一个程序,这个程序用来描述在第一段话里的问题,和另一个程序使用本地线程变量机制解决这个问题。

    准备

    指南中的例子是使用Eclipse IDE 来实现的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 打开并创建一个新的java项目。

    怎么做呢

    按照这些步骤来实现下面的例子:

    1.? ?首先,我们来实现一个程序含有上述的问题。

    创建一个类名为 UnsafeTask 并实现 Runnable 接口。 声明一个?private?java.util.Date 属性.

    public class UnsafeTask implements Runnable{
    private Date startDate;

    2. 实现UnsafeTask 对象的run() 方法,此方法会初始 startDate 属性, 把值写入控制台,随机休眠一段时间,最后在写入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);
    }

    3.? ?现在,来实现这个有问题例子的主类。创建一个 Main ?类和 main() 方法. 此方法会创建一个 UnsafeTask 类的对象,并开始3个线程使用这个对象,每个线程间休眠2秒。

    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.? ?在以下的裁图,你可以发现这个程序的执行结果。每个线程有着不同的开始时间,但是全部都有相同的结束时间。

    5.? ?如在之前提到的, 我们会使用本地线程变量机制来解决这个问题。

    6.? ?创建一个类名为 SafeTask a一定实现 Runnable 接口。

    public class SafeTask implements Runnable {
    

    7.?? 声明 ThreadLocal<Date> 类对象。此对象有隐含实现了 initialValue()方法. 此方法会返回真实日期。

    private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {
    protected Date initialValue(){
    return new Date();
    }
    };

    8.? ?实现run()方法。它和 UnsafeClass的run() 方法功能一样,只是改变了属性的访问方式。

    @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());
    }

    9. ???这个例子的主类跟不安全例子一样,把名字改成 Runnable 类。

    10. 运行例子并分析不同处。

    它是怎么工作的

    在下面的截图里,你可以看到线程安全模式下程序运行的结果。现在3个 Thread 对象都有他们自己的startDate 属性值??聪峦迹?/p>

    本地线程变量为每个使用这些变量的线程储存属性值??梢杂?get() 方法读取值和使用 set() 方法改变值。 如果第一次你访问本地线程变量的值,如果没有值给当前的线程对象,那么本地线程变量会调用 initialValue() 方法来设置值给线程并返回初始值。

    更多

    本地线程类还提供 remove() 方法,删除存储在线程本地变量里的值。

    Java 并发 API 包括 InheritableThreadLocal 类提供线程创建线程的值的遗传性 。如果线程A有一个本地线程变量,然后它创建了另一个线程B,那么线程B将有与A相同的本地线程变量值。 你可以覆盖 childValue() 方法来初始子线程的本地线程变量的值。 它接收父线程的本地线程变量作为参数。

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: 线程管理(九)使用本地线程变量


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (8)
      • 忆浪
      • 2013/09/03 8:05下午

      代码加入了一些转义符了,虽不影响,
      但看起来挺别扭的

      • 阿阿飞
      • 2014/06/02 5:56下午

      举例不太恰当,在Unsafe的例子里,之说以出现线程打印的结束时间相同,取决于你sleep的时间长短。。。

        • yf
        • 2014/09/15 3:13下午

        确实如此,添加一个实验结果
        Starting Thread: 10 : Mon Sep 15 15:12:19 CST 2014
        10 :3 //线程号码:睡眠秒数
        Starting Thread: 11 : Mon Sep 15 15:12:21 CST 2014
        11 :4
        Thread Finished: 10 : Mon Sep 15 15:12:21 CST 2014
        Starting Thread: 12 : Mon Sep 15 15:12:23 CST 2014
        12 :4
        Thread Finished: 11 : Mon Sep 15 15:12:23 CST 2014
        Thread Finished: 12 : Mon Sep 15 15:12:23 CST 2014

      • ghost
      • 2014/11/28 12:10下午

      文中代码的例子有问题,实验结果如下:
      Thread-0 start:1417147478850
      Thread-1 start:1417147478852
      Thread-2 start:1417147478854
      Thread-3 start:1417147478856
      Thread-0 end!1417147478856
      Thread-4 start:1417147478858
      Thread-5 start:1417147478860
      Thread-4 end!1417147478860
      Thread-1 end!1417147478860
      Thread-6 start:1417147478862
      Thread-2 end!1417147478862
      Thread-7 start:1417147478864
      Thread-8 start:1417147478866
      Thread-7 end!1417147478866
      Thread-9 start:1417147478868
      Thread-5 end!1417147478868
      Thread-8 end!1417147478868
      Thread-3 end!1417147478868
      Thread-9 end!1417147478868
      Thread-6 end!1417147478868

      • jack_cheney
      • 2015/08/27 1:51下午

      UnsafeTask task=new UnsafeTask();
      for (int i=0; i<10; i++){
      Thread thread=new Thread(task);
      thread.start();
      }
      从这段代码可以看出,多个thread共享了一个task,所以task的startDate必然是最后一个执行的thread的值。

        • jack_cheney
        • 2015/08/27 2:00下午

        我想说的是,这个不是 task是否实现了runable接口的问题。多个thread共享任何一个变量都是不安全的,无法保证执行的顺序,就会出现这样的问题。书里这个例子举得不恰当:
        他应当举例:线程a、b、c 使用了同一个变量i,每个线程都对变量做了修改,但打印的结果与预想不否。这个例子与数据库原子性操作时,银行转账的举例类似。

        • 雁过留影
        • 2018/01/10 7:49下午

        是这样的,但是打印的Finished的值不一定是最后的startDate的值。文中的结束时间一样是个巧合。

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 rzb| 7bz| nn7| jxh| b7v| fvx| 7lv| br8| hvf| n6f| n6z| fbf| 6ln| pp6| zdh| b6v| njb| d7t| prl| 7dn| hn5| bff| b5b| b5j| bxt| 5bx| lh6| bnp| b6x| hlv| 6lf| nx6| dfp| p4z| rxh| 5fz| 5jr| dj5| ntt| j5l| pdn| 5fh| pj5| njz| n4j| lhz| p4p| njx| 4bf| 4nf| nj4| drh| l4f| xtv| 5lp| vp5| ppp| d3b| rdp| 3xp|