A simple analysis of the use of the ThreadLocal class in Java thread programming

  • 2020-04-01 04:28:44
  • OfStack

An overview,
 
What is ThreadLocal? In fact, ThreadLocal is not a local implementation of a Thread; it is not a Thread, but a threadlocalvariable. It might be more appropriate to call it ThreadLocalVar. The function of ThreadLocal variable (ThreadLocal) is very simple, that is, to provide a copy of the value of a variable for each thread that USES the variable. As a special thread binding mechanism in Java, each thread can change its own copy independently without conflict with the copy of other threads.
 
From a thread's perspective, each thread keeps an implicit reference to a copy of its thread-local variable, as long as the thread is active and the ThreadLocal instance is accessible. After a thread disappears, all copies of its thread-local instance are garbage collected (unless there are other references to those copies).
 
Data accessed via ThreadLocal is always associated with the current thread, that is, the JVM binds a private local instance access space for each running thread, providing an isolation mechanism for the concurrent access problems that often occur in multithreaded environments.
 
How does ThreadLocal maintain a copy of the variable for each thread? The idea is simple: there is a Map in the ThreadLocal class that stores a copy of each thread's variable.
 
To summarize, for the problem of multi-threaded resource sharing, synchronization takes a time-for-space approach, while ThreadLocal takes a space-for-time approach. The former provides only one variable to be queued for access by different threads, while the latter provides one variable for each thread, so it can be accessed at the same time without affecting each other.
 
Ii. API description
 
ThreadLocal ()
                  Create a thread local variable.
 
T get ()
                  Returns the value in the current thread copy of the thread local variable, which is created and initialized if this is the first time the thread calls the method.
 
Protected  T the initialValue ()
                  Returns the initial value of the current thread of this thread local variable. This method is called at most once each time the thread is accessed to obtain each thread-local variable, when the thread first accesses the variable using the get() method. If the thread calls the set(T) method before the get method, the initialValue method is not called in the thread.
 
    If the implementation only returns null; If the programmer wants to initialize a thread-local variable to something other than null, he must subclass ThreadLocal and override this method. Typically, anonymous inner classes will be used. A typical implementation of initialValue invokes an appropriate constructor and returns the newly constructed object.
 
Void the remove ()
                  Removes the value of the thread-local variable. This may help reduce the storage requirements for thread-local variables. If this thread local variable is accessed again, it will have its initialValue by default.
 
Void the set value (T)
                  Sets the value in the current thread copy of this thread local variable to the specified value. Many applications do not require this functionality and rely only on the initialValue() method to set the value of the thread-local variable.
 
It is common to override the initialValue method in a program to give a specific initialValue.

Iii. 1. Understanding of ThreadLocal

ThreadLocal, many of which are called thread-local variables, and some of which are called thread-local storage, means the same thing. Many of you probably know that ThreadLocal creates a copy of a variable in each thread, so each thread can access its own internal replica variable.

This sentence seems easy to understand in the literal sense, but it is not so easy to understand in the real sense.

Let's start with an example:


class ConnectionManager {
   
  private static Connection connect = null;
   
  public static Connection openConnection() {
    if(connect == null){
      connect = DriverManager.getConnection();
    }
    return connect;
  }
   
  public static void closeConnection() {
    if(connect!=null)
      connect.close();
  }
}

  Assuming you have a database link management class, this code is fine in a single thread, but what if you use it in multiple threads? Obviously, there are thread-safety issues with using multiple threads: first, none of the two methods are synchronized, and it is likely that connect will be created multiple times in the openConnection method; Second, since connect is a Shared variable, it is necessary to use synchronization where connect is called to ensure thread safety, as it is likely that one thread is using connect for database operations and another thread is calling closeConnection to close the link.

So for thread-safety reasons, you have to synchronize the two methods of this code, and you need to synchronize where connect is called.

This can significantly affect program execution, because one thread is using connect for database operations while the others are waiting.

So let's take a closer look at this question, does this place need to share the connect variable? Actually, no. If there is a connect variable in each thread, access to the connect variable between threads is virtually independent, meaning that one thread does not need to care whether the other thread has modified the connect.

At this point, it might occur to some of your friends that since you don't need to share this variable between threads, you can simply do this by creating a database link for each method that needs to use a database connection, and then releasing the connection after the method is called. For example:


class ConnectionManager {
   
  private Connection connect = null;
   
  public Connection openConnection() {
    if(connect == null){
      connect = DriverManager.getConnection();
    }
    return connect;
  }
   
  public void closeConnection() {
    if(connect!=null)
      connect.close();
  }
}
 
 
class Dao{
  public void insert() {
    ConnectionManager connectionManager = new ConnectionManager();
    Connection connection = connectionManager.openConnection();
     
    //Operate with a connection
     
    connectionManager.closeConnection();
  }
}

  There's really nothing wrong with this, because each time you create a connection inside a method, there's no thread safety problem between threads. But this can have a fatal effect: it can put a lot of pressure on the server and seriously affect program performance. Because of the frequent opening and closing of database connections in methods, this can not only seriously affect program execution efficiency, but can also cause server stress.

So this case using ThreadLocal is perfect, because the ThreadLocal to the variable in each thread will create a copy of the inside each thread will have a the variable, and within the thread, can be used anywhere between threads each other, so there is no thread safety problem, also won't seriously affect the program execution performance.

Note, however, that while ThreadLocal can solve the above problem, because a copy is created in each thread, consider its resource consumption, such as memory footprint, to be larger than if ThreadLocal were not used.

Four, the instance,

Create a Bean and set the Bean properties through different thread objects to ensure the independence of each thread Bean object.
 



public class Student {
  private int age = 0;  //age
 
  public int getAge() {
    return this.age;
  }
 
  public void setAge(int age) {
    this.age = age;
  }
}
 

public class ThreadLocalDemo implements Runnable {
  //Create the thread-local variable studentLocal, which you will find later to save the Student object
  private final static ThreadLocal studentLocal = new ThreadLocal();
 
  public static void main(String[] agrs) {
    ThreadLocalDemo td = new ThreadLocalDemo();
    Thread t1 = new Thread(td, "a");
    Thread t2 = new Thread(td, "b");
    t1.start();
    t2.start();
  }
 
  public void run() {
    accessStudent();
  }
 
  
  public void accessStudent() {
    //Gets the name of the current thread
    String currentThreadName = Thread.currentThread().getName();
    System.out.println(currentThreadName + " is running!");
    //Generate a random number and print it
    Random random = new Random();
    int age = random.nextInt(100);
    System.out.println("thread " + currentThreadName + " set age to:" + age);
    // To get a Student Object and will random number age Inserts into an object property 
    Student student = getStudent();
    student.setAge(age);
    System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
    try {
      Thread.sleep(500);
    }
    catch (InterruptedException ex) {
      ex.printStackTrace();
    }
    System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
  }
 
  protected Student getStudent() {
    //Gets the local thread variable and casts it to the Student type
    Student student = (Student) studentLocal.get();
    //The first time the thread executes this method, studentlocal.get () is definitely null
    if (student == null) {
      //Create a Student object and save it to the local thread variable studentLocal
      student = new Student();
      studentLocal.set(student);
    }
    return student;
  }
}

 
Operation results:


a is running! 
thread a set age to:76 
b is running! 
thread b set age to:27 
thread a first read age is:76 
thread b first read age is:27 
thread a second read age is:76 
thread b second read age is:27 

 
You can see that the values of a and b are exactly the same when they are printed at different times. This program through the clever use of ThreadLocal, not only to achieve multi-thread concurrency, the game to consider the security of data.

V. general steps for ThreadLocal usage
 
1. In a multithreaded class (such as the ThreadDemo class), create a ThreadLocal object called threadXxx to hold the object that needs to be isolated between threads.
2. In the ThreadDemo class, create a method getXxx() to get the data to be isolated from the access. In the method, it is determined that if the ThreadLocal object is null, an object of the isolated access type should be new() and cast to the type to be applied.
3. In the run() method of the ThreadDemo class, the data to be manipulated is obtained by the getXxx() method, which ensures that each thread corresponds to a data object that is manipulated at any time.


Related articles: