41-案例实现
案例实现
根据以下步骤完成本案例。
1.首先实现一个程序来展现之前描述的问题。创建一个名为 UnsafeTask
的类,实现 Runnable
接口,并声明一个私有的 java.util.Date
型属性:
public class UnsafeTask implements Runnable{
private Date startDate;
2.实现该类的 run()
方法。该方法将初始化并打印 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
类。在 main()
方法中,创建一个 UnsafeTask
实例,并用 10
个线程对象执行该任务,每隔2s启动一个执行线程:
public class Main {
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.程序的执行结果如下图所示。可以看出,虽然每个线程都有不同的启动时间,但在线程结束时,这些时间值又发生了变化,因为线程进行了错误的写,例如,检查ID号为13的线程[3]:
5.正如之前所述,可以采用线程本地变量(thread-local variable)机制来解决这个问题。
6.创建一个名为 SafeTask
的类,并实现 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()
方法。虽然该方法的作用与 UnsafeTask
的 run()
方法相同,但是,它们访问 startDate
属性的方式不同,这里使用 startDate
对象的 get()
方法:
@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.该案例的 Main
类与之前的案例基本相同,区别在于 Runnable
类的名字不同。
10.运行案例并查看结果。