InheritableThreadLocal对象、线程池的使用

InheritableThreadLocal: 可继承的ThreadLocal

使用类InheritableThreadLocal可以在子线程中获得父线程继承下来的值

值继承


public class InheritableThreadLocalExt extends InheritableThreadLocal {
	@Override
	protected Object initialValue() {
		return System.currentTimeMillis();
	}
}
public class Tools {
 
	public static InheritableThreadLocalExt tl = new InheritableThreadLocalExt();
 
}


public class ThreadA extends Thread {
 
	@Override
	public void run() {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在ThreadA线程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
 
}
public class ThreadB extends Thread {
 
	@Override
	public void run() {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在ThreadB线程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
 
}
public class Run {
 
	public static void main(String[] args) {
		try {
			for (int i = 0; i < 10; i++) {
				System.out.println("在Main线程中取值=" + Tools.tl.get());
				Thread.sleep(100);
			}
			Thread.sleep(5000);
			ThreadA a = new ThreadA();
			ThreadB b = new ThreadB();
			a.start();
			b.start();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
 
}

测试结果:

InheritableThreadLocal对象、线程池的使用_第1张图片

值继承再修改

如果在继承的同事还可以对值进一步的处理就更好了. 可以覆盖父类的clidValue方法


public class InheritableThreadLocalExt extends InheritableThreadLocal {
	@Override
	protected Object initialValue() {
		return System.currentTimeMillis();
	}
 
	@Override
	protected Object childValue(Object parentValue) {
		return parentValue + " 我在子线程加的~!";
	}
}

InheritableThreadLocal对象、线程池的使用_第2张图片
但在使用 InheritableThreadLocalExt类需要注意一点的是,如果子线程在取得值的同时,主线程将InheritableThreadLocalExt中的值进行更改,那么子线程取到的值还是旧值.
当必须将在变量中维护的每个线程属性自动传输到创建的任何子线程时,可继承的线程局部变量将优先于普通线程局部变量使用。

线程池中ThreadLocal的使用

在线程池中
ThreadLocal对象的生命周期跟线程的生命周期一样长,那么如果将ThreadLocal对象和线程池一起使用,就可能会遇到这种情况:一个线程的ThreadLocal对象会和其他线程的ThreadLocal对象串掉,
如果使用的话,需要
两种操作:

一、直接在进入线程时remove操作。

二、使用后清空。
需要在finally块中调用remove 方法清除掉当前线程在ThreadLocal中存储的变量,避免线程复用导致的线程变量多个线程之间污染传递;

   private void ansyExcute(MedicalAdviceCatcheResultDTO catcheResultDTO, Map<Long, List<BqyzxxDO>> bqyzxxDOBySYXHMap
            , Map<String, String> map, String authorization) {
        bqyzxxDOBySYXHMap.forEach((k, v) -> {
            executor.execute(() -> {
                try {
                    AsyncRequestContext.setParrentToAnsyToken(authorization);
                    log.info("authorization" + authorization);
                    yzYzzxxExcuteThread.excute(k, v, map, catcheResultDTO);
                } catch (Exception e) {
                    log.error(Thread.currentThread().getId()+"线程执行异常!!!!!"+e.getMessage(), e);
                } finally {
                    AsyncRequestContext.removeAsyncToken();
                }

            });
        });
    }
    ```

```java
import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @Classname AsyncRequestContext
 * @Description 异步请求上下文
 * @Date 2021/4/28 18:18
 * @Created by DELL
 */

@Slf4j
public class AsyncRequestContext {
    /**
     * 线程池复上线文信息传递
     */
    public static ThreadLocal<RequestAttributes> tl = new TransmittableThreadLocal<>(); //这里采用TTL的实现

    /**
     * 异步线程池线程上下文信息,支持异步请求
     */
    public static InheritableThreadLocal<String> asyncToken = new InheritableThreadLocal<>();

    public  static String getParentToken(){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        RequestContextHolder.setRequestAttributes(attributes,true);
        HttpServletRequest httpServletRequest = attributes.getRequest();
        String authorization = httpServletRequest.getHeader("Authorization");
        log.info("Thread ID{},parent authorization{}",Thread.currentThread().getId(),authorization);
        return authorization;
    }



    public  static void setParrentToAnsyToken(String authorization){
        log.info("Ansy Thread ID{},set parent Thread Token{}",Thread.currentThread().getId(),authorization);
        if(StringUtils.isNotBlank(authorization)){
            AsyncRequestContext.asyncToken.set(authorization);
        }
    }

    public  static  void removeAsyncToken(){
        log.info("Ansy Thread ID{}, Thread Token{}",Thread.currentThread().getId(),AsyncRequestContext.asyncToken.get());
        AsyncRequestContext.asyncToken.remove();

    }

    public  static String getAnsyToken(){
     String  authorization=  AsyncRequestContext.asyncToken.get();
        log.info("Thread ID{},ansy authorization{}",Thread.currentThread().getId(),authorization);
        return authorization;
    }
}

线程池中变量的传递

异步线程,主线程的变量变量会随着主线程的结束而变量销毁,如果将主线程的变量存在ThreadLocal中或者InheritableThreadLocal,存在ThreadLocal或InheritableThreadLocal者中存的线程变量会随着主线程的生命周期而销毁。在子线程中调用get()方法得不到主线程存储的值;
因此子线程中显示传递

InheritableThreadLocal对象、线程池的使用_第3张图片
在子线程中塞入ThreadLocal 中,在子线程中需要的时候在从ThreadLocal中随时获取,在线程池中还需要线程执行完毕remove()掉

你可能感兴趣的:(java基础,#,线程,多线程)