并发程序一个重要方面就是共享数据。
这一点在继承了Thread类或实现了Runnable接口的对象中有着特殊的重要性。
如果你创建了一个实现了Runnable接口的类对象并且用这个对象开启了N个线程对象,那么所有这些线程对象共享同样的属性。
这意味着,如果你再某一线程中修改了属性值,所有其他线程将都能看到并受影响。有时候,你可能对每个线程拥有自己私有的属性感兴趣。这也正是Java并发API提供的一项机制,
即:thread-local variables 本地线程变量。
本例中,我们会开发一个共享变量程序和一个本地线程变量程序作为对比。
UnsafeTask.java
package com.dylan.thread.ch1.c09.task;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Class that shows the problem generate when some Thread objects
* share a data structure
*
*/
public class UnsafeTask implements Runnable{
/**
* Date shared by all threads
*/
private Date startDate;
/**
* Main method of the class. Saves the start date and writes
* it to the console when it starts and when it ends
*/
@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);
}
}
Main.java
package com.dylan.thread.ch1.c09.core;
import com.dylan.thread.ch1.c09.task.UnsafeTask;
import java.util.concurrent.TimeUnit;
/**
* Main class of the UnsafeTask. Creates a Runnable task and
* three Thread objects that run it.
*
*/
public class Main {
/**
* Main method of the UnsafeTaks. Creates a Runnable task and
* three Thread objects that run it.
* @param args
*/
public static void main(String[] args) {
// Creates the unsafe task
UnsafeTask task=new UnsafeTask();
// Throw three Thread objects
for (int i=0; i<3; i++){
Thread thread=new Thread(task);
thread.start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行输出:
Starting Thread: 10 : Mon Apr 30 20:15:01 CST 2018
Starting Thread: 11 : Mon Apr 30 20:15:03 CST 2018
Starting Thread: 12 : Mon Apr 30 20:15:05 CST 2018
Thread Finished: 10 : Mon Apr 30 20:15:05 CST 2018
Thread Finished: 11 : Mon Apr 30 20:15:05 CST 2018
Thread Finished: 12 : Mon Apr 30 20:15:05 CST 2018
可以看到线程结束时间都和最后一个执行结束的线程12的时间一致,说明3个线程的时间变量是共享的。
SafeTask.java
package com.dylan.thread.ch1.c09.task;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Class that shows the usage of ThreadLocal variables to share
* data between Thread objects
*
*/
public class SafeTask implements Runnable {
/**
* ThreadLocal shared between the Thread objects
*/
private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {
protected Date initialValue(){
return new Date();
}
};
/**
* Main method of the class
*/
@Override
public void run() {
// Writes the start date
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();
}
// Writes the start date
System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());
}
}
SafeMain.java
package com.dylan.thread.ch1.c09.core;
import com.dylan.thread.ch1.c09.task.SafeTask;
import java.util.concurrent.TimeUnit;
/**
* Main class of the example.
*
*/
public class SafeMain {
/**
* Main method of the example
* @param args
*/
public static void main(String[] args) {
// Creates a task
SafeTask task=new SafeTask();
// Creates and start three Thread objects for that Task
for (int i=0; i<3; i++){
Thread thread=new Thread(task);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.start();
}
}
}
运行输出:
Starting Thread: 10 : Mon Apr 30 20:12:45 CST 2018
Starting Thread: 11 : Mon Apr 30 20:12:47 CST 2018
Thread Finished: 10 : Mon Apr 30 20:12:45 CST 2018
Starting Thread: 12 : Mon Apr 30 20:12:49 CST 2018
Thread Finished: 12 : Mon Apr 30 20:12:49 CST 2018
Thread Finished: 11 : Mon Apr 30 20:12:47 CST 2018
可以看到3个线程的时间都不一样,说明没有共享。