preface
The content of this article is quite large, nearly 8000 words, can be read according to the table of contents according to your needs.
After reading this article, if you want to learn more about the basics of concurrent programming, check out this one:
Other basics of concurrent programming – The White Primer: juejin.cn/post/696659…
Three ways to create a thread
There are three ways to create threads in Java, namely, implementing the Run method of the Runnable interface, inheriting the Thread class and rewriting the run method, and using the FutureTask method.
Inherit the Thread class and override the run methods
When the thread object is created, the thread is not started until the start method is called.
When the start method is called, the thread does not execute immediately. Instead, it is in a ready state. This state means that the thread has acquired resources other than CPU resources and will not be running until it has acquired CPU resources. Once the run method completes, the thread is terminated.
Advantages:
- Instead of thread.currentthread (), use this to retrieve the currentThread from within the run method.
- For easy parameter passing, you can add member variables to subclasses, use the set method to set parameters, or pass them through constructors.
Disadvantages:
- Java does not support multiple inheritance, and if you inherit Thread, you cannot base on other classes.
- There is no separation of task and code, and Runable does not limit multiple copies of task code when multiple threads perform the same task.
Code implementation:
Public static class MyThread extends Thread{@override public void run(){public void run(){ System.out.println("I am a child thread"); }} public static void main(String[] args){MyThread thread = new MyThread(); // Start the thread thread.start(); }}Copy the code
Implement the Run method of the Runnable interface
Disadvantages: If you want to pass parameters, only variables declared final in the main thread are used.
Code implementation
public class ThreadTest { public static class RunableTask implements Runnable{ @Override public void run(){ System.out.println("I am a child thread"); Public static void main(String[] args) throws InterruptedException{RunableTask task = new RunableTask(); new Thread(task).start(); new Thread(task).start(); }}Copy the code
Use FutureTask
One drawback of both approaches is that the task does not return a value.
Code implementation
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; Public class ThreadTest {// Create a task class, Runable public static class CallerTask implements Callable<String> {@override public String Call () throws Exception{ return "hello"; }} public static void main(String[] args) throws InterruptedException{// Create asynchronous task FutureTask<String> FutureTask = new FutureTask<>(new CallerTask()); New Thread(futureTask).start(); String result = futureTask.get(); // Wait for the task to complete and return the result. System.out.println(result); }catch(ExecutionException e){ e.printStackTrace(); }}}Copy the code
Thread notifications and waits
The Object class in Java is the parent class of all classes. In view of the inheritance mechanism, Java puts the methods needed by all classes into the Object class, which includes the notification and wait functions described in this section — wait(), notify() and notifyAll().
1. Wait functions
When a thread calls wait() on a shared variable, the thread blocks and does not return until one of the following things happens: (1) Other threads call notify() or notifyAll() on the shared object;
(2) Another thread calls the interrupt() method of that thread, which returns InterruptedException.
Note: if the calling wait () method of thread without prior access to the object of the monitor lock, call the wait () method is the calling thread will be thrown IllegalMonitorStateException anomalies.
A thread can acquire a monitor lock for a shared variable:
(1) Synchronized synchronized code block is executed, using the shared variable as a parameter.
Synchronized {//doSomething}Copy the code
(2) a method that calls the shared variable and uses the synchronized modifier
synchronized void add(int a, int b){
//doSomething
}
Copy the code
False awaken
If a thread is not notified by another thread using notify() or notifyAll(), or is interrupted, or waits for a timeout, it changes from suspended to runnable (that is, woken up).
False wake up rarely happens in practice, but to prevent it from happening, the method is to constantly test whether the condition for the thread to wake up is met, and if not, continue to wait, that is to say, call wait() method in a loop to prevent. The condition for exiting the loop is that the condition for waking up the process is met.
Synchronized (obj){while(condition not satisfied){obj. Wait (); }}Copy the code
The wait method only releases locks on the current shared variable
The current thread only releases locks on the current shared variable after calling wait(). If the current thread holds locks on other shared variables, these locks will not be released.
For example,
Public class ThreadTest {private static volatile Object resourceA = new Object(); private static volatile Object resourceB = new Object(); Public static void main(String[] args) throws InterruptedException{// Create Thread threadA Thread threadA = new Thread(new Runnable() {@override public void run() {try{synchronized(resourceA){ System.out.println("threadA get resourceA lock"); synchronized(resourceB){ System.out.println("threadA get resourceB lock"); System.out.println("threadA release resourceA lock"); resourceA.wait(); } } }catch (Exception e){ e.printStackTrace(); }}}); ThreadB threadB = new Thread(new Runnable() {@override public void run(){try{// Sleep for 1 SEC thread.sleep (1000); Println ("threadB get resourceA lock"); synchronized(resourceA){system.out.println ("threadB get resourceA lock"); System.out.println("threadB try get resouceB lock..." ); Println ("threadB get resourceB lock"); synchronized(resourceB){system.out.println ("threadB get resourceB lock"); System.out.println("threadB release resourceA lock"); resourceA.wait(); } } }catch(Exception e){ e.printStackTrace(); }}}); Threada.start (); threadB.start(); Threada.join (); // Wait for two threads to terminate. threadB.join(); System.out.println("main over"); }}Copy the code
Running results:
Results analysis: In this code, thread A and thread B are started in the main function. In order to make thread A acquire the lock first, thread B sleep for 1s. Thread A acquire the lock on the shared resourceA and resourceB. It then blocks itself by calling wait() on resourceA, and releases the lock on the acquired resourceA.
If thread A has not called wait() to release the lock, thread B will be blocked. When thread A releases the lock on resourceA, thread B will acquire the lock on resourceA. Then try to obtain the lock on resourceB. Since thread A called wait() on resourceA, thread A suspended itself and did not release the lock on the acquired resourceB, thread B blocked when attempting to acquire the lock on resourceB.
This proves that when a thread calls its wait() method on a shared object, the current thread releases the lock on the current shared object, and the monitor lock on other shared objects held by the current thread is not released.
Interrupt blocking suspends a thread and throws an exception
When a thread calling the wait() method on a shared object is blocked and suspended, it throws InterruptedException and returns if another thread interrupts it.
The sample
public class ThreadTest { static Object obj = new Object(); Public static void main(String[] args) throws InterruptedException{Thread threadA = new Thread(new Runnable(){ public void run(){ try{ System.out.println("---begin---"); Synchronized (obj){obj.wait(); synchronized(obj){obj. } System.out.println("---end---"); }catch(Exception e){ e.printStackTrace(); }}}); threadA.start(); Thread.sleep(1000); System.out.println("---begin interrupt threadA---"); threadA.interrupt(); ThreadA system.out.println ("-- end interrupt threadA-- "); threadA system.out.println ("-- end interrupt threadA-- "); }}Copy the code
Running results:
Code analysis:
In the code above, threadA suspends itself by calling the wait() method on the shared object obj, and then the main thread interrupts the threadA thread after sleeping for 1s. After interruption threadA obj. Wait () throws Java lang. InterruptedException and terminate in return.
2. Wait (long timeout)
Wait () has one more timeout parameter than wait(). If a thread calling the suspended method on a shared object is not awakened by another thread calling the notify() or notifyAll() method within the specified timeout ms, the function will still return due to timeout.
If timeout is set to 0, it has the same effect as the wait method because wait(0) is called inside the wait method.
If timeout < 0, IllegalArgumentException is thrown.
3. Wait (long timeout, int nanos
The wait(long timeout) function is called internally, and the following code increments timeout by 1 only if nanos>0
The source code
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
Copy the code
4. Notify () function
A thread that calls notify() on a shared object wakes up a thread that has been suspended after the employee wait methods were dropped on that shared variable. Multiple threads may be waiting on a shared variable, and it is random which waiting thread is awakened.
In addition, the awakened thread not immediately return and continue from the wait method, it must be in after the monitor lock access to the Shared object can be returned, is awakened threads it released after the monitor lock on the Shared variables, awakened the thread does not necessarily will get to the Shared object monitor lock, this is because the thread is also need to compete with other threads the lock, Execution can continue only if the thread competes with the monitor that shares the variable.
Similar to wait series method, only the current thread after the monitor lock access to the Shared variables, only can call the Shared variables notify () method, otherwise will be thrown IllegalMonitorStateException anomalies.
5. NotifyAll () function
The notifyAll() method wakes up all threads suspended on this shared variable due to calls to the WAIT series of methods.
Note that dropping the employee notifyAll() method on a shared variable will only wake up the threads that called the WAIT series before calling the method and were placed in the shared variable wait set. If, after notifyAll() is called, a thread that calls the wait() method of the shared variable is put into the blocking set, the thread is not woken up.
The sample
Public class NotifyAndNotifyAll {private static volatile Object resourceA = new Object(); Public static void main(String[] args) throws InterruptedException{Thread threadA = new Thread(new Runnable() { Public void run(){synchronized(resourceA){system.out.println ("threadA get resourceA lock"); system.out.println ("threadA get resourceA lock"); try{ System.out.println("threadA begin wait"); resourceA.wait(); System.out.println("threadA end wait"); }catch(Exception e){ e.printStackTrace(); }}}}); ThreadB = new Thread(new Runnable() {public void run(){synchronized(resourceA){ System.out.println("threadB get resourceA lock"); try{ System.out.println("threadB begin wait"); resourceA.wait(); System.out.println("threadB end wait"); }catch(Exception e){ e.printStackTrace(); }}}}); ThreadC = new Thread(new Runnable() {public void run(){synchronized(resourceA){synchronized(resourceA){ System.out.println("threadC begin notify"); //resourceA.notify(); // Wake up a thread resourcea.notifyall () blocked by calling wait on the shared resourceA; // Wake up all threads blocked by calling wait on the shared resourceA}}}); Threada.start (); threadB.start(); Thread.sleep(1000); // main thread sleep 1s threadc.start (); Threada.join (); threadB.join(); threadC.join(); System.out.println("main over"); }}Copy the code
The result of the notify() call
Only one thread, A, is awakened, and thread, B, is not
The run result of calling notifyAll()
Both thread A and thread B are woken up
Wait for thread to execute terminated join method
Sometimes during development, we need to wait for several things to complete before we can proceed, such as multiple threads loading resources and waiting for multiple threads to complete the load before we can summarize the process.
The Thread class has a join method that does this. The wait notification methods described earlier are methods in the Object class, while the Join methods are provided directly by the Thread class. Join is a method that takes no arguments and returns void.
Example:
public static void main(String[] args) throws InterruptedException{ Thread threadA = new Thread(new Runnable(){ public void run(){ try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } System.out.println("chile threadA over!" ); }}); Thread threadB = new Thread(new Runnable(){ public void run(){ try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("chile threadA over!" ); }}); Threada.start (); threadB.start(); System.out.println("wait all child thread over!" ); // Wait for the child thread to complete, return threada.join (); threadB.join(); System.out.println("all child thread over !" ); }Copy the code
The above code creates two child threadA and threadB threads on the main thread, starts them, and calls their join() methods, respectively. The main thread will block after calling threadA.join() and wait for threadA to return. The threadb.join () method is then blocked and returns after threadB completes.
The results
If thread A blocks after calling thread B’s join method, thread A returns with InterruptedException when interrupted by another thread calling interrupt().
Let the thread sleep method
The Thread class has a static sleep method. When an executing Thread calls the Thread’s sleep method, the calling Thread temporarily cedes execution rights for a specified period of time. This means that it does not participate in CPU scheduling during this period, but it still holds monitor resources, such as locks. When the specified time is up, the function returns normally, the thread is in the ready state, and then the CPU is scheduled to resume running after it has acquired CPU resources.
If the thread is interrupted during sleep by another thread calling its interrupt() method, the thread returns with InterruptedException where the sleep method was called.
When thread.sleep (Long millis) is called, an IllegalArgumentException is thrown if the value of the millis argument is negative.
For example:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Public class SleepTest {// Create an exclusive Lock private static final Lock Lock = new ReentrantLock(); Public static void main(String[] args) throws InterruptedException{Thread threadA = new Thread(new Runnable(){ Public void run(){// Get the exclusive lock lock.lock(); try{ System.out.println("child threadA is in sleep"); Thread.sleep(10000); System.out.println("child threadA is in awaked"); }catch(Exception e){ e.printStackTrace(); }finally {// unlock lock.unlock(); }}}); ThreadB = new Thread(new Runnable(){public void run(){// Obtain the exclusive lock lock.lock(); try{ System.out.println("child threadB is in sleep"); Thread.sleep(10000); System.out.println("child threadB is in awaked"); }catch(Exception e){ e.printStackTrace(); }finally{// unlock lock.unlock(); }}}); Threada.start (); threadB.start(); }}Copy the code
The results
The code above first creates an exclusive lock and then creates two threads, each of which internally acquires the lock and then releases the lock when it is finished sleeping. First of all, no matter how many times you execute the above code thread A prints first or thread B prints first, there’s no crossover between thread A and thread B. So thread A gets the lock first, so thread A will print one line and then call sleep to sleep itself for 10 seconds, The exclusive lock is held by thread A for 10 seconds while thread A sleeps, and thread B blocks until thread A wakes up and releases the lock.
Yield the CPU to execute
Thread has a static yield method. When a Thread calls the yield method, it is actually signaling to the Thread scheduler that the current Thread is requesting its CPU usage, but the Thread scheduler can ignore the hint unconditionally.
When a thread calls yield, the current thread will yield CPU usage and then be in the ready state. The scheduler will fetch the thread with the highest priority from the ready queue, or it may schedule CPU execution to the thread that just yielded PU.
Thread interrupt
Thread interrupt in Java is a cooperative mode between threads. The execution of the thread can not be terminated directly by setting the interrupt flag of the thread, but the interrupted thread will handle it according to the interrupt state.
Here are three ways to interrupt a thread:
1. Void interrupt()
Interrupt A thread, such as when thread A is running. While thread A is running, thread B can call threadInterrupt () to set thread A’s interrupt flag to true and return immediately. Setting the flag is just setting the flag, thread A is not actually interrupted, it will continue to execute.
If thread A (A) blocks because of calling the Wait, join(), or sleep() methods, then thread B (B) calls the thread iterrupt () method. Thread A will throw InterruptedException where it calls these methods and return.
2. Boolean isInterrupted() method:
Checks whether the current thread is interrupted, returning true if it is, false otherwise, without removing the interrupt flag
3. Boolean interrupted() method:
Checks if the current thread is interrupted, returning true if it is, false otherwise. Unlike lnterrupted, this method clears the interrupt flag if it detects that the current Thread is interrupted, and it is static and can be called directly from the Thread class. In the following code you can see that interrupted () gets the interrupt flag of the current calling thread, not the instance object that called the interrupted () method.
Public static Boolean interrupted(){return currentThread().isinterrupted (true); }Copy the code
Understand thread context switches
1. The concept
Number of threads in a multithreaded programming, is generally greater than the number of CPU, and can only be used by one thread per CPU the same time, in order to make the user feel are multiple threads at the same time, the allocation of CPU resources using the time slice rotation policy, also is to give each thread allocate a time slice, thread CPU to perform the task in time. When a thread runs out of time slices, it becomes ready and frees up the CPU for another thread. This is called a context switch, switching from the context of the current thread to another thread.
When the thread context is switched, the execution scene of the current thread needs to be saved. When the thread is executed again, the execution thread is restored according to the execution scene information of the error.
Thread context switch timing: the current thread is ready when the CPU time slice is used up, and the current thread is interrupted by other threads.
2. How to reduce context switching
There are four ways to reduce context switching, namely lockless concurrent programming, CAS algorithm, using minimum threads, and using coroutines. It may be a little vague, but here are some of the ways.
Lockless concurrent programming: When multiple threads compete for locks, context switch will be triggered. Therefore, when multiple threads process data, some concurrency can be used to avoid the use of locks. For example, the ID of data can be modded according to the Hash algorithm, and different threads process different segments of data.
CAS algorithm: Java’s Atomic package uses the CAS algorithm to update data without locking.
Use minimal threads: Avoid creating threads that are not needed, such as a few tasks, but many threads are created to process them, which results in a large number of threads waiting.
Coroutine: Performs scheduling of multiple tasks in a single thread and maintains switching between tasks in a single thread.
3. Challenges posed by resource constraints
3.1 What are resource constraints
In concurrent programming, the execution speed of the program is limited by computer hardware resources or software resources. Hardware resources limit bandwidth upload/download speed, disk read/write speed, and CPU processing speed. Software resource limitations include database knowledge and socket connections.
3.2 Problems caused by resource constraints
In concurrent programming, code execution speed is principle is serial execution part of the code into concurrent execution, but if the serial code will be a period of concurrent execution, because of limited resources, is still in serial implementation, this program not only speed up execution, it will more slowly, because of increased context switch and resource scheduling data.
3.3 How can I Solve the Resource Limitation problem
For hardware resource constraints, consider clustering concurrent execution of programs. Since resources are limited on a single machine, let the program run on multiple machines.
For software resource constraints, you can use resource pools to reuse resources. Such as using connection pooling to reuse database and Socket connections, or establishing only one connection when calling each other’s WebService interface for data.
3.4 Concurrent programming under resource constraints
Adjust the program’s concurrency for different resource limits, such as a file-downloading program that depends on two resources ———— bandwidth and disk read/write speed. When there is a database operation, the number of database connections is involved. If the SQL statement is executed very fast and the number of threads is much larger than the number of database connections, some threads will be blocked waiting for the database connection.
Thread deadlocks
Deadlock refers to the phenomenon that two or more threads are waiting for each other during execution due to competing for resources. Without external force, these threads will wait for each other and cannot continue to run.
1. Four conditions must be met for a deadlock to occur
Mutually exclusive: A thread uses a resource that it has acquired exclusively, that is, only one thread occupies the resource at the same time. If another thread requests the resource at this point, the requester can only wait until the possessor thread releases the resource.
Request and hold condition: a thread has already held at least one resource, but then makes a new resource request, and the new resource has been occupied by another thread, so the current thread will be blocked, but the block does not release its acquired resources.
Inalienable condition: A thread cannot preempt a resource until it uses it up. It can release the resource only after it uses it up.
Loop waiting condition: refers to the existence of a thread ———— resource loop chain when a deadlock occurs.
Here is an example of thread deadlocks:
Public class DeadLockTest {// Create resource private static Object resourceA = new Object(); private static Object resourceB = new Object(); Public static void main(String[] args){threadA = new Thread(new Runnable(){public void run(){ synchronized(resourceA){ System.out.println(Thread.currentThread()+" get resourceA"); try{ Thread.sleep(1000); // Sleep the thread for 1s}catch(InterruptedException e){e.printStackTrace(); } System.out.println(Thread.currentThread()+" waiting get resourceB"); synchronized(resourceB){ System.out.println(Thread.currentThread()+" get resourceB"); }}}}); ThreadB = new Thread(new Runnable(){public void run(){synchronized(resourceB){synchronized(resourceB){ System.out.println(Thread.currentThread()+" get resourceB"); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread()+" waiting get resourceA"); synchronized(resourceA){ System.out.println(Thread.currentThread()+" get resourceA"); }}}}); Threada.start (); threadB.start(); }}Copy the code
The results
When thread A calls synchronized(resource A) to acquire the resourceA monitor lock and release it, thread A can use the synchronized(resource A) method to obtain the resourceA monitor lock. Thread B attempts to obtain the resource by calling synchronized(resourceA). Thread B can obtain the resource only when thread A releases the lock, which satisfies the mutual exclusion of resources.
Thread A obtains the monitor lock resource on resourceA through the synchronized(resourceA) method, and then waits to obtain the recourceB monitor lock resource through the synchronized(res oureB) method, which constitutes the request and holding condition.
After thread A obtains the monitor lock resource on resourA, the resource will not be robbed by thread B. Only when thread releases recourceA resource, it will give up the right to hold the resource, which constitutes the inalienable condition of the resource.
Thread A holds objectA resources and waits for objectB resources, while thread B holds objectB resources and waits for objectA resources. This creates A loop wait condition, so thread A and thread B enter A deadlock state.
2. How to avoid thread deadlocks
To avoid deadlocks, at least one condition necessary for deadlock construction must be broken. Currently, only the request-and-hold and loop wait conditions can be broken.
Thread B can avoid the deadlock by changing the order in which thread B applies for resources. Thread B can avoid the deadlock by changing the order in which thread B applies for resources.
ThreadB = new Thread(new Runnable(){public void run(){synchronized(resourceA){synchronized(resourceA){ System.out.println(Thread.currentThread()+" get resourceB"); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread()+" waiting get resourceA"); synchronized(resourceB){ System.out.println(Thread.currentThread()+" get resourceA"); }}}});Copy the code
The results
It can be found that thread B applies for A resourceA first and then for A resourceB. To obtain A resourceA, thread A has to wait for thread A to release the resource. Therefore, when thread A releases the resource resourceA after running, thread B can get the resource from the blocked state to the ready state. It then starts running after obtaining CPU resources.
The orderliness of resources breaks the request for resources and holds conditions and loop wait conditions, thus avoiding deadlocks.
Daemon threads and user threads
There are two types of threads in Java: daemon threads and user threads. The main function is called when the JVM starts, and the thread in which the main function resides is a user thread. Many daemon threads, such as garbage collection threads, are also started inside the JVM.
So what’s the difference between a daemon thread and a user thread? One difference is that the JVM exits normally when the last non-daemon thread terminates, regardless of whether or not the daemon thread is currently terminated, meaning that the JVM exits regardless of whether or not the daemon thread terminates. The implication is that the JVM normally does not exit as long as a user thread is not finished.
1. How do I create a daemon thread?
public static void main(String[] args){ Thread daemonThread = new Thread(new Runnable(){ public void run(){ } }); // Set it to daemonthread.setdaemon (true); daemonThread.start(); }Copy the code
As you can see from the code, you only need to set the thread’s daemon parameter to true.
2. Timing of daemon and user threads
If you want the JVM to end as soon as the main thread ends, you can create the thread as a daemon thread, or if you want the child thread to continue working after the main thread ends and then the JVM process ends, you can set the child thread as a user thread.
Ten, ThreadLocal
ThreadLocal is a JDK package that provides thread-local variables, meaning that if you create a ThreadLocal variable, each thread that accesses it will have a local copy of that variable.
When multiple threads manipulate this variable, they are actually manipulating the variable in their own local memory, thus avoiding thread-safety issues. Once a ThreadLocal variable is created, each thread copies a variable into its local memory.
1. An example of ThreadLocal
Public class ThreadLocalTest {//print function static void print(String STR){//1.1 Prints the value of localVariable in the local memory of the current thread System.out.println(str+":"+localVariable.get()); Localvariable.remove (); localvariable.remove (); Static ThreadLocal<String> localVariable = new ThreadLocal<>(); static ThreadLocal<String> localVariable = new ThreadLocal<>(); Public static void main(String[] args){one Thread threadOne = new Thread(new Runnable(){public void run(){ Localvariable. set("threadOne local variable"); Print ("threadOne"); System.out.println("threadOne remove after: "+ localvariable.get ()); }}); ThreadTwo = new Thread(new Runnable(){public void run(){localvariable. set("threadTwo local ") variable"); Print ("threadTwo"); System.out.println("threadTwo remove after: "+ localvariable.get ()); }}); Threadone.start (); threadTwo.start(); }}Copy the code
The results
In thread One, set the value of localVariable repeatedly, which is actually set a copy of thread One’s local memory, which thread Two cannot access. Then call the print function. Print function obtains the value of localVariable in the local memory of the current thread (thread One) through the get function and outputs it, and then takes out the localVariable in the local memory of the current thread.
Thread Two executes like thread One.
2. Methods of get, set, and remove are introduced
All three methods are called by ThreadLocal variables. The get method is used to get the local memory variable value of the corresponding thread, the set method is used to set the local memory variable value of the corresponding thread, and the remove method is used to remove the local variables from the corresponding thread’s threadLocals.
Each thread’s local variables are stored in the thread’s own memory variable, threadLocal. If the current thread never dies, these local variables will always exist, so it can cause memory overflow. Call the remove method of ThreadLocal to output local variables from the corresponding thread’s threadLocals.
3. ThreadLocal does not support inheritance
Look at the following example:
Public static ThreadLocal<String> ThreadLocal = new ThreadLocal<String>(); Public static void main(String[] args){// Set threadlocale.set (" Hello world"); Thread Thread = new Thread(new Runnable(){public void run(){system.out.println (" Thread: "+threadLocal.get()); // Print null because the child thread cannot get the value set by the parent thread}}); thread.start(); System.out.println("main: "+threadLocal.get()); }}Copy the code
The results
That is, the same ThreadLocal variable that is set in the parent thread cannot be fetched in the child thread.
As described above, this should be normal, because the current thread is the hread thread when the get method is called, while the main thread is called to set the thread variable. The two threads are different and return null when the child thread calls.
4. InheritableThreadLocal class
InheritableThreadLocal inherits from ThreadLocal and provides a feature that allows child threads to access local variables set in the parent thread.
In the example in point 3, thread can also access the values set by the main thread by modifying the code that created the thread:
public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
Modified to
public static ThreadLocal<String> threadLocal = new InheritableThreadLocal<String>();
Extension: There are other ways to access threadLocal variables in a parent thread besides using InheritableThreadLocal, such as passing variables in the parent thread when the thread is created and copying them into the child thread.
At the end of the article: thank you for reading, the article does not specify the source code implementation of some methods, if you want a more in-depth understanding, recommended to see the Beauty of Java Concurrent programming.