《Java程序员面试秘笈》—— 1.10 线程局部变量的使用

简介: 线程局部变量分别为每个线程存储了各自的属性值,并提供给每个线程使用。你可以使用get()方法读取这个值,并用set()方法设置这个值。如果线程是第一次访问线程局部变量,线程局部变量可能还没有为它存储值,这个时候initialValue()方法就会被调用,并且返回当前的时间值。

本节书摘来异步社区《Java 7并发编程实战手册》一书中的第1章,第1.10节,作者:【西】Javier Fernández González,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.10 线程局部变量的使用

共享数据是并发程序最核心的问题之一,对于继承了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);

}`
3.实现这个有问题的应用程序的主程序。创建一个包含main()方法的Main类。这个方法将创建一个UnsafeTask类对象,用它作为传入参数创建10个线程对象并启动这10个线程,每个线程的启动间隔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.在下面的截屏中,你将看到这个程序执行的结果。每个线程有一个不同的开始时间,但是当它们结束时,三个线程都有相同的startDate属性值。

<div style="text-align: center"><img src="https://yqfile.alicdn.com/99c4b18a096f0ba73d8b725a51b90efe41ab1aee.png" width="" height="">
</div>


5.如之前提到的,我们将使用线程局部变量机制来解决这个问题。

6.创建一个SafeTask类,用以实现Runnable接口

public class SafeTask implements Runnable {
7.声明一个ThreadLocal对象。这个对象是在initialValue()方法中隐式实现的。这个方法将返回当前日期。

private static ThreadLocal startDate= new

ThreadLocal<Date>() {
    protected Date initialValue(){
        return new Date();
    }
};```

8.实现run()方法。它跟UnsafeTask类的run()方法实现了一样的功能,但是访问startDate属性的方式改变了。

@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个线程对象都有它们自己的startDate属性值。

线程局部变量分别为每个线程存储了各自的属性值,并提供给每个线程使用。你可以使用get()方法读取这个值,并用set()方法设置这个值。如果线程是第一次访问线程局部变量,线程局部变量可能还没有为它存储值,这个时候initialValue()方法就会被调用,并且返回当前的时间值。

<div style="text-align: center"><img src="https://yqfile.alicdn.com/c0e418d3668796c2de1d761bbc5873ebbf84c340.png" width="" height="">
</div>

更多信息
相关文章
|
4天前
|
XML 缓存 Java
Java大厂面试题
Java大厂面试题
14 0
|
4天前
|
存储 安全 Java
Java大厂面试题
Java大厂面试题
10 0
|
4天前
|
存储 安全 Java
Java大厂面试题
Java大厂面试题
13 0
|
4天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
5天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
5天前
|
安全 Java
就只说 3 个 Java 面试题 —— 02
就只说 3 个 Java 面试题 —— 02
17 0
|
5天前
|
存储 安全 Java
就只说 3 个 Java 面试题
就只说 3 个 Java 面试题
10 0
|
5天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
5天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
6天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
22 0

热门文章

最新文章