Shared data is one of the most critical features of concurrent programs. This is a very important aspect whether it is an object that inherits the Thread class or an object that implements the Runnable interface.
If you create an object of a class that implements the Runnable interface and use that object to start a series of threads, all of those threads share the same properties. In other words, if one thread modifies a property, all remaining threads are affected by the change.
Sometimes, we prefer to use it alone within a thread instead of sharing it with other threads started with the same object. The Java concurrency interface provides a very clear mechanism to meet this requirement, called thread-local variables. The performance of this mechanism is also very impressive.
know it
Follow the steps shown below to complete the sample program.
1. First, implement a program with the above problems. Create a class named UnsafeTask and implement the Runnable interface. Declare a private property of type java.util.Date in the class. The code is as follows:
Copy the code code as follows:
public class UnsafeTask implements Runnable {
private Date startDate;
2. Implement the run() method of UnsafeTask, which instantiates the startDate attribute and outputs its value to the console. Sleep for a random period of time and then print the value of the startDate attribute to the console again. The code is as follows:
Copy the code code as follows:
@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. Implement the main class of the problematic program. Create a class with a main() method, UnsafeMain. In the main() method, create an UnsafeTask object and use this object to create 10 Thread objects to start 10 threads. In the middle of each thread, sleep for 2 seconds. The code is as follows:
Copy the code code as follows:
public class UnsafeMain {
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. From the above logic, each thread has a different startup time. However, according to the output log below, there are many identical time values. as follows:
Copy the code code as follows:
Starting Thread: 9 : Sun Sep 29 23:31:08 CST 2013
Starting Thread: 10 : Sun Sep 29 23:31:10 CST 2013
Starting Thread: 11 : Sun Sep 29 23:31:12 CST 2013
Starting Thread: 12 : Sun Sep 29 23:31:14 CST 2013
Thread Finished: 9 : Sun Sep 29 23:31:14 CST 2013
Starting Thread: 13 : Sun Sep 29 23:31:16 CST 2013
Thread Finished: 10 : Sun Sep 29 23:31:16 CST 2013
Starting Thread: 14 : Sun Sep 29 23:31:18 CST 2013
Thread Finished: 11 : Sun Sep 29 23:31:18 CST 2013
Starting Thread: 15 : Sun Sep 29 23:31:20 CST 2013
Thread Finished: 12: Sun Sep 29 23:31:20 CST 2013
Starting Thread: 16 : Sun Sep 29 23:31:22 CST 2013
Starting Thread: 17: Sun Sep 29 23:31:24 CST 2013
Thread Finished: 17 : Sun Sep 29 23:31:24 CST 2013
Thread Finished: 15 : Sun Sep 29 23:31:24 CST 2013
Thread Finished: 13 : Sun Sep 29 23:31:24 CST 2013
Starting Thread: 18 : Sun Sep 29 23:31:26 CST 2013
Thread Finished: 14 : Sun Sep 29 23:31:26 CST 2013
Thread Finished: 18 : Sun Sep 29 23:31:26 CST 2013
Thread Finished: 16 : Sun Sep 29 23:31:26 CST 2013
5. As shown above, we are going to use the thread-local variables mechanism to solve this problem.
6. Create a class named SafeTask and implement the Runnable interface. The code is as follows:
Copy the code code as follows:
public class SafeTask implements Runnable {
7. Declare an object of type ThreadLocal<Date>. When the object is instantiated, the initialValue() method is overridden and the actual date value is returned in this method. The code is as follows:
Copy the code code as follows:
private static ThreadLocal<Date> startDate = new
ThreadLocal<Date>() {
@Override
protected Date initialValue() {
return new Date();
}
};
8. Implement the run() method of the SafeTask class. This method is the same as the run() method of UnsafeTask, except that the method of the startDate attribute is slightly adjusted. The code is as follows:
Copy the code code as follows:
@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. The main class of this safe example is basically the same as the main class of the non-safe program, except that UnsafeTask needs to be modified to SafeTask. The specific code is as follows:
Copy the code code as follows:
public class SafeMain {
public static void main(String[] args) {
SafeTask task = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(task);
thread.start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
10. Run the program and analyze the differences between the two inputs.
In order to standardize the naming of classes, the naming of the main class in this article is slightly different from the original text. In addition, the original program and text description are inconsistent. It must be a clerical error.
know why
Below is the execution result of the security example. From the results, it can be easily seen that each thread has a startDate attribute value belonging to the respective thread. The program input is as follows:
Copy the code code as follows:
Starting Thread: 9 : Sun Sep 29 23:52:17 CST 2013
Starting Thread: 10 : Sun Sep 29 23:52:19 CST 2013
Starting Thread: 11 : Sun Sep 29 23:52:21 CST 2013
Thread Finished: 10: Sun Sep 29 23:52:19 CST 2013
Starting Thread: 12 : Sun Sep 29 23:52:23 CST 2013
Thread Finished: 11 : Sun Sep 29 23:52:21 CST 2013
Starting Thread: 13 : Sun Sep 29 23:52:25 CST 2013
Thread Finished: 9 : Sun Sep 29 23:52:17 CST 2013
Starting Thread: 14 : Sun Sep 29 23:52:27 CST 2013
Starting Thread: 15 : Sun Sep 29 23:52:29 CST 2013
Thread Finished: 13 : Sun Sep 29 23:52:25 CST 2013
Starting Thread: 16 : Sun Sep 29 23:52:31 CST 2013
Thread Finished: 14 : Sun Sep 29 23:52:27 CST 2013
Starting Thread: 17: Sun Sep 29 23:52:33 CST 2013
Thread Finished: 12: Sun Sep 29 23:52:23 CST 2013
Thread Finished: 16 : Sun Sep 29 23:52:31 CST 2013
Thread Finished: 15 : Sun Sep 29 23:52:29 CST 2013
Starting Thread: 18 : Sun Sep 29 23:52:35 CST 2013
Thread Finished: 17 : Sun Sep 29 23:52:33 CST 2013
Thread Finished: 18 : Sun Sep 29 23:52:35 CST 2013
Thread local variables store a copy of the property for each thread. You can use ThreadLocal's get() method to obtain the value of a variable, and use the set() method to set the value of a variable. If a thread-local variable is accessed for the first time and the variable has not yet been assigned a value, the initialValue() method is called to initialize a value for each thread.
never ending
The ThreadLocal class also provides the remove() method to delete the local variable value stored in the thread that calls this method.
In addition, the Java concurrency API also provides the InheritableThreadLocal class, which allows the child thread to receive the initial values of all inheritable thread local variables to obtain the value owned by the parent thread. If thread A has a thread local variable, when thread A creates thread B, thread B will have the same thread local variable as thread A. You can also override childValue() to initialize the thread local variables of the child thread. This method will accept the value of a thread-local variable passed as a parameter from the parent thread.
Use doctrine
This article is translated from "Java 7 Concurrency Cookbook" (D Gua Ge stole it as "Java7 Concurrency Example Collection") and is only used as learning materials. It may not be used for any commercial purposes without authorization.
Small success
Below is a complete version of all the code included in the examples in this section.
Complete code of UnsafeTask class:
Copy the code code as follows:
package com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Examples where thread safety cannot be guaranteed
* Date: 2013-09-23
* Time: 23:58
*/
public class UnsafeTask implements Runnable {
private Date 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);
}
}
Complete code of UnsafeMain class:
Copy the code code as follows:
package com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.concurrent.TimeUnit;
/**
* Unsafe thread example
* Date: 2013-09-24
* Time: 00:04
*/
public class UnsafeMain {
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();
}
}
}
}
Complete code of SafeTask class:
Copy the code code as follows:
package com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Use thread local variables to ensure thread safety
* Date: 2013-09-29
* Time: 23:34
*/
public class SafeTask implements Runnable {
private static ThreadLocal<Date> startDate = new
ThreadLocal<Date>() {
@Override
protected Date initialValue() {
return new Date();
}
};
@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());
}
}
Complete code of SafeMain class:
Copy the code code as follows:
package com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.concurrent.TimeUnit;
/**
* Safe thread example
* Date: 2013-09-24
* Time: 00:04
*/
public class SafeMain {
public static void main(String[] args) {
SafeTask task = new SafeTask();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(task);
thread.start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}