多个线程进行上下文操作时会引起,线程安全问题,可通过ThreadLocal的特性,为每个线程开启副本,解决此类问题。
存放信息的对象
//存放信息的上下文对象
public class Context {
//随便设置几个属性
private String name;
private String cardId;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getCardId() {
return cardId;
}
}
用ThreadLocal和单例模式为每个线程创建各自独立的上下文信息
/**
* ThreadLocal 为每个线程创建当前线程的副本,可保线程安全。
*/
public final class ActionContext {
//通过线程副本存放context对象信息。
private static final ThreadLocal<Context> threadLocal = new ThreadLocal<Context>() {
@Override
protected Context initialValue() {
return new Context();
}
};
//单例模式,确保线程安全。
private ActionContext(){}
private static class ContextHolder {
private final static ActionContext actionContext = new ActionContext();
}
//调用单例方法。
public static ActionContext getActionContext() {
return ContextHolder.actionContext;
}
//调用 ThreadLocal 的get方法。
public Context getContext() {
return threadLocal.get();
}
}
在一些方法中将信息塞到ThreadLocal的上下文对象中
//模拟从某处拿到信息,塞到context中。
public class QueryFromDBAction {
//假设拿到一些数据
public void execute() {
String name = "Jun " + Thread.currentThread().getName();
//把数据放入context对象中。
ActionContext.getActionContext().getContext().setName(name);
}
}
//模拟从某处拿到信息,塞到context中。
public class QueryFromHttpAction {
public void execute() {
//获取当前线程的id。
String cardId =""+ Thread.currentThread().getId();
//将信息放入context中。
ActionContext.getActionContext().getContext().setCardId(cardId);
}
}
线程的逻辑执行单元
//逻辑执行单元
public class ExecutionTask implements Runnable {
//假设从DB查询数据
private QueryFromDBAction queryDbAction = new QueryFromDBAction();
//假设从别的端口拿数据
private QueryFromHttpAction httpAction = new QueryFromHttpAction();
@Override
public void run() {
//模拟塞数据到上下文中
queryDbAction.execute();
//模拟塞数据到上下文中
httpAction.execute();
//从 ActionContext 的ThreadLocal<Context>线程副本中取出context对象
Context context = ActionContext.getActionContext().getContext();
System.out.println("线程名字: " + context.getName() + " 上下文ID: " + context.getCardId());
}
}
Test_Main
public static void main(String[] args) {
//开启线程,传入逻辑执行单元。
IntStream.range(1, 5)
.forEach(i ->
new Thread(new ExecutionTask()).start()
);
}