并发程序一个重要方面就是共享数据。
这一点在继承了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个线程的时间都不一样,说明没有共享。