The Beauty of Java Concurrent Programming Chapter 1 The Basics of Concurrent Programming (Reading Notes)

1–50 sides

Basics of java concurrent programming

what is thread

process:

It is a running activity of the code on the data set, and it is the basic unit for the system to allocate and schedule resources.

thread:

It is an execution path of a process, a process has at least one thread, and multiple threads in a process share the resources of the process.

Thread is the basic unit of CPU allocation

stack:

Each thread has its own stack resource, which is used to store the local variables of the thread. These local variables are private to the thread and cannot be accessed by other threads. In addition, the stack can also be used to store the call stack frame of the thread.

heap:

The heap is the largest memory in a process. The heap is shared by all threads in the process and is allocated when the process is created. The heap mainly stores object instances created using the new operation.

Method area:

Stores information such as classes loaded by the JVM, constants, and static variables, and is also shared by threads.

Thread creation and running

There are three ways to create threads in java

    • Implement the run method of the Runnable interface
    • Inherit the Thread class and override the run method
    • Use the FutureTask method
/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发.线程的创建
 * @date 2023/4/2 15:53
 *
 *
 *
 *
 *
 *
 *
 *
 * 1.使用继承的好处是方便传参,可以在子类里面添加成员方法
 *   通过set或者构造函数进行参数传递
 * 2.使用Runnable则只能使用主线程里面被声明的final变量.不好的地方是java不支持多继承
 * 3.继承Thread那么子类不能再继承其他类 而Runnable没有这个限制
 * 4.前两个方法都不支持返回参数,最后的Futuretask方式可以
 * 
 */
public class ThreadTest {


    /**
     * 第一种实现多线程的方式
     *
     * 优点:
     *      在run()方法内获取当前线程不需要使用Thread.currentThread()方法,直接使用this就可以获取当前线程
     * 缺点:
     *      java不支持多继承,如果继承Thread就不能继承其他父类
     *      任务和代码没分离,当多个线程执行一样的任务时候需要多份认任务代码
     *      任务方法没有返回值
     */
    public static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("I am a Thread");
        }
    }


    /**
     * 第二种创建线程的方法
     *
     * 优点:
     *  任务跟代码分离开
     * 缺点:
     *  任务方法没有返回值
     */
    public static class RunableTask implements Runnable{
        @Override
        public void run() {
            System.out.println("I am a Thread2");
        }
    }



    public static class CallTask implements Callable<String>{
        @Override
        public String call() throws Exception {
            return "hello Thread";
        }
    }



    public static void main(String[] args) throws InterruptedException{
        /**
         *创建线程的时候其实还没有成功启动,只有执行了线程的start方法的时候才算真正启动成功
         * 用操作系统的角度来看创建线程的时候其实处在了就绪态(已经获取了除了CPU之外的其他资源)
         * 执行start会执行线程的run方法,就获得了cpu从就绪态转到了运行态
         * run方法结束标志该线程运行完成处于终止状态
         *
         */
        MyThread myThread = new MyThread();

        //线程启动
        myThread.start();;

        RunableTask runableTask = new RunableTask();
        new Thread((runableTask)).start();;
        new Thread((runableTask)).start();


        FutureTask<String> futureTask = new FutureTask<>(new CallTask());
        new Thread(futureTask).start();

        try {
            String string = futureTask.get();
            System.out.println(string);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

Thread notification and waiting

wait() function

When a thread calls the wait() method of the shared thread, the thread will be suspended by the group match, and will not return until the following situations occur

      1. Other threads call notify() or notifyAll() of the shared thread
      2. Other threads call interrupt() of this thread, and this thread throws an interrupt exception and returns

Note: If the monitor lock of the object is not acquired in advance when the wait() method is called, an IllegalMonitorException will be thrown when the method is called

Method to acquire a monitor lock on a shared variable

When executing the synchronized synchronization code block, use the shared variable as a parameter
synchronized (共享变量){
    //代码逻辑
}
When calling the shared variable method, and the method uses synchronized to modify
synchronized void add(int a,int b){
    //代码逻辑
}

spurious wakeup

A thread has not been notified by other threads calling notify(), notifyAll() method, and has not been interrupted, waiting for timeout, it changes from suspended state to running state

To avoid this spurious wakeup

Method: Continuously test whether the condition for the thread to be awakened is met, if not, continue to wait, and exit the loop if the wake-up condition is met

synchronized (obj){
    while (条件不满足){
        obj.wait();
    }
}

Note: After the current thread calls the wait() method of the shared variable, only the lock on the current shared thread variable will be released. If the current thread still holds the lock of other threads, it will not be released.

When a thread calls the wait() method of the shared object and is blocked and suspended, if other threads interrupt the thread, the thread will throw InterruptedException and return

/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发
 * @date 2023/4/2 20:16
 */
public class WaitNotifyInterupt {
    static Object object=new Object();
    public static void main(String[] args) throws InterruptedException{
        Thread threadA=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("--------begin--------");
                synchronized (object){
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        threadA.start();
        Thread.sleep(1000);
        System.out.println("begin interrput threadA");
        threadA.interrupt();
        System.out.println("end interrupt threadA---");
    }

}

wait(long timeout) function

Compared with the previous Bund() method, there is one more timeout parameter

difference:

If a thread calls this method of the shared object and is not awakened by other threads calling the notify() and notifyAll() methods of the shared thread within the specified timeout period, it will return because of the timeout

If called with a negative number, an IllegalArgumentException will be thrown

wait(long timeout, int nanos) function

public final void wait(long timeout,int nanos) throws InterruptedException{
    if(nanos<0){
        throw new IllegalAccessException("timeout value is negative");
    }
    if(nanos<0||nanos>999999){
        throw new IllegalAccessException("nanoscond timeout value out of range");
    }
    if(nanos>0){
        timeout++;
    }
    wait(timeout);
}

notify() function

It will wake up a thread that is suspended after calling the wait() series of methods on the shared variable. There may be many blocked threads waiting on a shared variable, and the thread that is woken up is random

The awakened thread cannot return and run from the wait() method immediately, the thread must acquire the monitor lock on the shared object before returning

If the monitor lock is not acquired, an IllegalMonitorStateException will be thrown

notifyAll() function

Compared with the previous method, this method will release all the threads above the shared variable that are suspended due to calls to the wait() series of methods

public class NotifyAllTest {
    private static volatile Object resourceA=new Object();

    public static void main(String[] args) throws InterruptedException{
        Thread threadA=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    System.out.println("threadA get resocuseA lock");
                    try {
                        resourceA.wait();
                        System.out.println("threadA end wait");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }


                }
            }
        });
        Thread threadB=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    System.out.println("threadB get resocuseA lock");
                    try {
                        resourceA.wait();
                        System.out.println("threadB end wait");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }


                }
            }
        });
        Thread threadC=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    System.out.println("thread begin notify");
                    resourceA.notifyAll();

                }
            }
        });

        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        threadC.start();
        threadA.join();
        threadB.join();
        threadC.join();

        System.out.println("over");
    }
}

Note: Calling notifyAll() on a shared variable will only wake up threads that have called the wait series of functions before this method and are placed in the shared variable waiting set. It will not wake up and call the wait series of functions after this method.

Waiting thread 6373 waiting

The join method that waits for the thread execution to terminate

When you need to wait for multiple events to complete before continuing to execute, you can use the join() method provided by the Thread method

This method is a method with no parameters and no return value.

/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发.Test
 * @date 2023/4/12 23:31
 */
public class JoinTest {
    public static void main(String[] args) throws InterruptedException{
        Thread threadOne=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child threadOne over");
            }
        });


        Thread threadTwo =new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child threanTwo over");
            }
        });

        threadOne.start();
        threadTwo.start();

        System.out.println("wait all child thread over ");

        threadOne.join();
        threadTwo.join();

        System.out.println("all child over");
    }
}

Thread A will be blocked after calling the join method of thread B. When other threads call the interrupt() method of thread A to interrupt thread A, A will throw an InterruptedException

public class InterruptedExceptionTest {
    public static void main(String[] args) throws InterruptedException{
        Thread threadOne =new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("threadOne begin run!!!");
                for(; ; ){

                }
            }
        });


        final Thread mainThread=Thread.currentThread();



        Thread threadTwo =new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                mainThread.interrupt();
            }
        });



        threadOne.start();

        threadTwo.start();


        try {
            threadOne.join();
        } catch (InterruptedException e) {
            System.out.println("main thread "+e);
        }
    }
}

Let the thread sleep all sleep methods

Sleep is a static method of Thread. Calling this method will temporarily give up the execution right of the specified time, that is, it will not participate in CPU scheduling, but the monitor resource (lock) owned by the thread is still held and cannot be given up. When the sleep time is up, it will return normally, which is equivalent to changing from the blocked state to the ready state, participating in CPU scheduling, and entering the running state after obtaining CPU resources. However, if other threads call the thread's Interrupt() method to interrupt the thread during sleep, then the method will throw an InterruptedException and return when calling sleep.

/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发.Test
 * @date 2023/4/12 23:38
 */
public class InterruptedExceptionTest {
    
    //创建一个独占锁
    private static final Lock lock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    System.out.println("child threadA is in sleep");
                    Thread.sleep(1000);
                    System.out.println("child threadA is in awaked");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });


        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    System.out.println("child threadB is in sleep");
                    Thread.sleep(1000);
                    System.out.println("child threadB is in awake");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });


        threadOne.start();

        threadTwo.start();

    }
    
}

Yield method to give up cpu execution right

Yield is a static method of Thread. When a thread calls the yield method, it means that the current thread request of the thread scheduler gives up its own cpu usage, but the thread scheduler can ignore this hint.

When used, it means that the thread itself does not want to use the unused part of the time slice, implying that the thread scheduler can now perform the next round of thread scheduling. The current thread surrenders the right to use the CPU, and then it is in the ready state. The thread scheduler will obtain a thread with the highest thread priority from the thread ready queue, or it may schedule the thread that just gave up the CPU to obtain the execution right of the CPU.

Summarize:

The difference between sleep and yield methods

When a thread calls the sleep method, the calling thread will be blocked and suspended for a specified time, during which the thread scheduler will not schedule the program.

When yield is called, the thread will give up its remaining time slice. It is not blocked and suspended, but is in a ready state. The thread scheduler may schedule the current thread to execute at the next scheduling.

thread interrupt

Thread interruption is an inter-thread cooperation mode. Setting the interruption flag of a thread does not directly terminate the execution of the thread, but the interrupted thread handles itself according to the interruption status.

  • void Interrupt(): interrupt thread For example, when thread a is running, thread b can call the interrupt() method of thread a to set the interrupt flag of a to true and return. Setting the flag is just setting the flag. Thread a is not interrupted, and it will continue to execute. If thread a calls the wait series of functions, the join method or sleep method is blocked and suspended. At this time, if thread b calls the interrupt of a () method, thread a will throw InterruptedException when calling these methods
  • boolean isInterrupted() : Check if the current thread is interrupted. Return true if yes, otherwise return false
public boolean isInterrupted(){
   //传递false,说明不清除中断标志
     return isIntrrrupted( false);

}
  • boolean interrupted() : Check whether the current thread is interrupted, and return true if it is. The difference is that this method finds that the current thread is interrupted, it will clear the interrupt flag, and this method is a static method, which can be called directly by Thread. Inside interrupted(), the interrupt flag of the current thread is obtained instead of calling the instance object of the interrupted() method. interrupt flag.
public static boolean Interrupted{
//清除中断标志
return currentThread.isInterrupted(true);
}

An example of graceful exit from Interrupt

@Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()&&more work to do){
                //do more work
            }
        }catch (InterruptedException e){
            //thread was interrupted during sleep or wait
        }finally {
            //cleanup if required 
        }
    }

An example of judging whether a thread is terminated based on the interrupt flag

public static void main(String[] args) throws InterruptedException{
    Thread thread=new Thread(new Runnable() {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread()+"hello");
            }
        }
    });
    
    
    thread.start();
    
    
    Thread.sleep(1000);
    System.out.println("main thread interrupt thread");
		thread.interrupt();
    
    thread.join();
    System.out.println("main is over");
}

When a thread waits for some specific conditions to arrive, it generally calls the sleep function, wait series of functions or join functions to block and suspend the current thread. If the condition from blocking to active state is met in advance, at this time, the interrupt method of the thread can be called to force The sleep method throws an InterruptedException and returns, and the thread returns to the active state

public class InterruptedOrIsInterrupted {
    public static void main(String[] args) throws InterruptedException{
        Thread threadOne=new Thread(new Runnable() {
            @Override
            public void run() {
                for(; ;){

                }
            }
        });


        threadOne.start();

        threadOne.interrupt();

        System.out.println("isInterrupted:"+threadOne.isInterrupted());

//        System.out.println("isInterrupted"+threadOne.interrupted());

        System.out.println("isInterrupted:"+Thread.interrupted());

        System.out.println("isInterrupted:"+threadOne.isInterrupted());

        threadOne.join();
        System.out.println("main thread is over");
    }
}
/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发
 * @date 2023/4/13 9:11
 */
public class InterruptedOrIsInterruptedTwo {
    public static void main(String[] args) throws InterruptedException{
        Thread threadOne=new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {

                }
                System.out.println("threadTwo isInterrupted:" + Thread.currentThread().isInterrupted());
            }
        });



        threadOne.start();

        threadOne.interrupt();


        threadOne.join();
        System.out.println("main thread is over");
    }
}

Understanding thread context switching

In multi-threading, the number of threads is generally greater than the number of CPUs, and each CPU can only be used by one thread at a time. In order to let users feel that multiple threads are being used at the same time, the allocation of CPUs uses time slice rotation. Strategy. Each thread is assigned a time slice, and the thread occupies the CPU to perform tasks within the allocated time slice. When the thread slice is used up, it will be in a ready state and let the CPU be occupied by other threads.

When context switching, it is necessary to save the execution site of the current thread, and restore the execution site according to the saved execution site information when executing again

Switch timing:

    • The CPU time slice of the current thread is used up and is in the ready state
    • When the current thread is interrupted by another thread

thread deadlock

What is thread deadlock

Deadlock refers to the phenomenon that two or more threads wait for each other due to robbing resources during the execution process. Under the action of no external force, these threads will wait forever and cannot run

Four necessary conditions for a deadlock to occur

  1. mutually exclusive condition

Threads use the acquired resources exclusively, and the resource can only be occupied by one thread at the same time. If there are other threads requesting the resource at this time, the requester can only wait until the occupied resource is released

  1. request holding conditions

Refers to a thread that already occupies at least one resource, but makes a new resource request, and the new resource has been occupied by other threads, so the current thread is blocked, but it does not release the resources it has obtained while blocking

  1. inalienable condition

It means that the resources acquired by the thread cannot be preempted by other threads before they are used up, and can only be used by other threads after they are released after they are used up.

  1. loop wait condition

It means that when a deadlock occurs, there must be a circular chain of one thread and one resource

/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发.Test
 * @date 2023/4/13 9:35
 */
public class DeadLockTest2 {
    private static Object resourceA=new Object();
    private static Object resourceB=new Object();

    public static void main(String[] args) throws InterruptedException{
        Thread threadA=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    System.out.println((Thread.currentThread() + "get ResourceA"));
                    try{
                        Thread.sleep(1000);
                        
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread()+"wait get sourceB");
                    synchronized (resourceB){
                        System.out.println(Thread.currentThread()+"get ResourceB");
                        
                    }
                };
            }
        });


        Thread threadB=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceB){
                    System.out.println((Thread.currentThread() + "get ResourceB"));
                    try{
                        Thread.sleep(1000);

                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread()+"wait get sourceA");
                    synchronized (resourceB){
                        System.out.println(Thread.currentThread()+"get ResourceA");

                    }
                };
            }
        });
        
        
        
        
    }
}

avoid thread deadlock

It is only necessary to destroy at least one necessary condition for deadlock, but currently only the request and hold and loop wait conditions can be destroyed

In fact, deadlocks are also closely related to the order in which resources are applied for. Using the orderly principle of resources can destroy and avoid deadlocks.

Example: Modify thread B of the above code

Thread threadB=new Thread(new Runnable() {
    @Override
    public void run() {
        synchronized (resourceA){
            System.out.println((Thread.currentThread() + "get ResourceB"));
            try{
                Thread.sleep(1000);

            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"wait get sourceA");
            synchronized (resourceB){
                System.out.println(Thread.currentThread()+"get ResourceA");

            }
        };
    }
});

Daemon threads and user threads

  • daemon thread (daemon thread)
  • user thread (user thread)

The main function is a user thread that will call the main thread when the JVM starts, but in fact it also calls many other daemon threads

the difference between the two

When the last non-daemon thread ends, the JVM will exit normally, regardless of whether it is currently a daemon thread, that is to say, the daemon thread does not affect the exit of the JVM. That is to say, as long as there is a user thread that has not ended, the JVM will not normally will quit

/**
 * @author xingchen
 * @version V1.0
 * @Package com.并发.Test
 * @date 2023/4/13 16:44
 */
public class DaemonTest {
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });

        /**
         * 只需要设置参数为true就开启了守护线程
         * 
         */
        thread.setDaemon(true);
        thread.start();
    }
}

Give an example to understand the difference between daemon threads and user threads

public class DaemonOrUserTest {

    /**
     *thread线程里面是一个无限循环,运行之后主线程已经结束,但是jvm还没有退出
     * 说明父线程结束后,子线程还可以继续存在,也就是子线程的生命周期并不受父线程的影响
     * 也说明了在用户线程还存在的情况下JVM进程并不会终止
     *
     *
     *
     *
     * 设置成用户线程之后一旦主线程停止之后,jvm发现已经不存在用户线程了,就会终止JVN进程
     * 如果用户进程已经结束,但是守护线程还说运行,这个时候JVM不需要等待守护线程停止就直接结束JVM进程
     *
     *
     *
     *
     * main线程运行结束之后,JVM会自动启动一个DestroyJavaVm  的线程,该线程等待所有的用户线程结束后
     * 终止JVM进程
     *
     * 
     */
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                for (; ;){

                }
            }
        });

        thread.setDaemon(true);
        thread.start();
        System.out.println("main thread is over");
    }



}

Summarize

If you want the JVM to end immediately after the main thread ends, you can set it as a daemon thread when creating a process, otherwise set it as a user thread

ThreadLocal

Concurrency issues are prone to occur when multiple threads access a decommissioned shared variable, especially when multiple threads need to write to a shared variable. In order to ensure thread safety, the general user needs to perform proper synchronization when accessing the shared variable.

Synchronization generally uses locking, but this method increases the burden on users.

Then after creating a variable, each thread accesses the variable of its own thread when accessing it . ThreadLocal in this section is for this purpose.

ThreadLocal

Provided by JDK, it provides thread local variables. If a ThreadLocal variable is created, each thread that accesses this variable will have a local copy of this variable

When multiple threads operate on this variable, the actual operation is the variable in their own local memory, thus avoiding thread safety issues

use case

public class ThreadLocalTest {
    static void print(String str){
        System.out.println(str+":"+localVariable.get());
        //localVariable.remove();

    }
    static ThreadLocal<String> localVariable=new ThreadLocal<>();
    public static void main(String[] agrs){
        Thread threadOne=new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("threadOne local variable");
                print("threadOne");
                System.out.println("threadOne remove after"+":"+localVariable.get());
            }
        });
        Thread threadTwo=new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("threadTwo local variable");
                print("threadOne");
                System.out.println("threadTwo remove after"+":"+localVariable.get());
            }
        });



        threadOne.start();
        threadTwo.start();
        

    }
}

Realization principle

set method

public void set(T value){
    Thread t=Thread.currentThread();
    ThreadLoalMap map=getMap(t);
    if(map!=null){
        map.set(this,value);
        
    }else {
        createMap(t,value);
    }
}
ThreadLocalMap getMap(Thread t){
    return t.threadLocals;
}

getMap(t)

  • The function of the method is to obtain the thread's own variable threadLocals, and the threadlocal variable is bound to the member variable of the thread
  • If the return value of the individual Map(T) is not empty, set the value to threadLocals
  • If the return value is empty, it means that the set method is called for the first time. At this time, the threadLocals variable of the current thread is created.

createMap(t,value)

Create a threadLocals variable for the current thread

void createMap(Thread t,T firstValue){
    t.threadLocals=new ThreadLocalMap(this,firstValue);
}

T get() method

public void get(){

    //获取当前线程
    Thread t=Thread.currentThread();
    //获取当前线程的threadLocxals变量
    ThreadLocalMap map=getMap(t);
    //如果ThreadLocals不为空,则返回对应的本地变量
    if(map!=null){
        ThreadLocalMap.Entry e=map.getEntry(this);
    }
    if(e!=null){
        @SuppressWarnings("unchecked")
         T result=(T) e.value;
        return result;
    }
    //为空初始化当前线程的ThreadLocals成员变量
    return setInitialValue;
}

private T setInitialValue(){
    //初始化为null
    T value =initialValue;
    Thread t=Thread.currentThread();
    ThreadLocalMap map=getMap(t);
    //如果当前线程的本地变量不为空
    if(map!=null){
        map.set(this,value);

    }else {
        //如果当前线程的本地变量为空 则创建
        createMap(t,value);

    }
    return value;
}
private T initialValue(){
    return null;
}

void remove()

/**
 * 如果当前线程的threadLocals变量不为空,则删除当前线程中指定ThreadLocal实例的本地变量
 
 */
public void remove(){
    ThreadLocalMap map=getMap(Thread.currentThread());
    if(m!=null){
        m.remove(this);
        
    }
    
}

does not support inheritance

public class TestThreadLocal1 {
    public static ThreadLocal<String> threadLocal=new ThreadLocal<>();
    public static void main(String[] args){
        threadLocal.set("hello world");
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread:"+threadLocal.get());
                
            }
        });
        thread.start();
        System.out.println("main:"+threadLocal.get());
    }
}

After the same ThreadLocal variable is set in the parent thread, it cannot be obtained in the child thread

The get method called in the child thread is the current thread, and the set method called is the main thread set

Both are different threads

InheritableThreadLocal class

Inherited from ThreadLocal, it provides a feature that allows child threads to access local variables set in the parent thread

public class InheritableThreadLocal<T> extends ThreadLocal{
    protected T childValue(T parentValue){
        return parentValue;
    }
    ThreadLocalMap.getMap(Thread t){
        return t.inheritableThreadLocals;
    }
    void create(Thread t,T firstValue){
        t.inheritableLocals=new TestThreadLocalMap(this,firstValue);
    }
}

Summarize:

The InheritableThreadLocal class rewrites the code so that the local variables are saved in the inheritableThreadLocals variable of the specific thread. Then when the thread sets the variable through the set or get() method of the InheritableThreadLocal class instance, the InheritableThreadLocals variable of the current thread will be created. When the parent thread creates a child thread , the constructor will copy the local variable in the InheritableThreadLocals variable of the parent thread and save it in the InheritableThreadLocals variable of the child thread.

Guess you like

Origin blog.csdn.net/weixin_60257072/article/details/130142503