Java concurrent programming example of nine: use of local thread variables

  • 2020-04-01 03:39:20
  • OfStack

Sharing data is one of the most critical features of concurrent programs. This is a very important aspect for both objects that inherit from the Thread class and objects that implement the Runnable interface.

If an object is created for a class that implements the Runnable interface and a series of threads are started with that object, all of them share the same properties. In other words, if one thread modifies a property, all other threads are affected by the change.

Sometimes, we prefer to be able to use it alone within a thread, rather than sharing it with other threads that are started with the same object. The Java concurrency interface provides a very clear mechanism for meeting this requirement, called local thread variables. The performance of this mechanism is also considerable.

learning

Complete the sample program by following the steps shown below.

1. First, implement a program with the above problems. Create a class called UnsafeTask and implement the Runnable interface. Declare a private property of type java.util.date in the class. The code is as follows:


public class UnsafeTask implements Runnable {
    private Date startDate;

2. Implement UnsafeTask's run() method, 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:


@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 : %sn",
            Thread.currentThread().getId(), startDate);
}

3. Implement the main class of the problem program. Create a class with the main() method, UnsafeMain. In the main() method, create an UnsafeTask object and use it to create 10 Thread objects to start 10 threads. In the middle of each thread, sleep for 2 seconds. The code is 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, many of the same values appear. 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 earlier, we're going to use the thread-local variables mechanism to solve this problem.

6. Create a class called SafeTask and implement the Runnable interface. The code is as follows:


public class SafeTask implements Runnable {

Declare a ThreadLocal< Date> Object of type that overrides the initialValue() method when instantiated, returning the actual date value in the method. The code is 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 UnsafeTask's run() method, except that the method of the startDate attribute is slightly tweaked. The code is as follows:


@Override
public void run() {
    System.out.printf("Starting Thread: %s : %sn",
            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 : %sn",
            Thread.currentThread().getId(), startDate.get());
}

9. The main class of the security example is basically the same as the main class of the non-security program, except that you need to change the UnsafeTask to SafeTask. The specific code is 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 difference between the two inputs.

In order to standardize the naming of classes, the main classes in this article are named a little differently from the original. In addition, the original program and text description is inconsistent. It should be a slip of the pen.

Know why

The following is the execution result of the security example. From the results, it is easy to see that each thread has a value of the startDate attribute belonging to its own thread. Program input is 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

The thread local variable stores a copy of the property for each thread. You can use ThreadLocal's get() method to get the value of the variable, and set() method to set the value of the variable. If the thread local variable is accessed for the first time and has not been assigned a value, the initialValue() method is called to initialize a value for each thread.

endless

The ThreadLocal class also provides the remove() method to remove the value of the local variable stored in the thread calling the method.

In addition, the Java concurrency API provides the InheritableThreadLocal class so that child threads can receive the initial value of all inheritable thread-local variables to get the value that the parent thread has. If thread A has A thread-local variable, thread B will have the same thread-local variable as thread A when thread A creates thread B. You can also override childValue() to initialize the thread local variable for the child thread. This method accepts the value of a thread-local variable passed as a parameter from the parent thread.

si

This article is a translation from the Java7 Concurrency Cookbook (D jago to Java7 Concurrency examples) and is intended for use only as a learning material. Not to be used in any commercial activities without authorization.

Small has becomes

Here is the complete version of all the code included in this section's example.

Complete code for the UnsafeTask class:


package com.diguage.books.concurrencycookbook.chapter1.recipe9; import java.util.Date;
import java.util.concurrent.TimeUnit; /**
 * Examples where thread safety is not 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 : %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 : %sn",
                Thread.currentThread().getId(), startDate);
    }
}

UnsafeMain class complete code:


package com.diguage.books.concurrencycookbook.chapter1.recipe9; import java.util.concurrent.TimeUnit; /**
 * Examples of unsafe threads
 * 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 for the SafeTask class:


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 : %sn",
                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 : %sn",
                Thread.currentThread().getId(), startDate.get());
    }
}

Complete code for the SafeMain class:


package com.diguage.books.concurrencycookbook.chapter1.recipe9; import java.util.concurrent.TimeUnit; /**
 * A 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();
            }
        }
    }
}


Related articles: