Boc*_*909 6 java multithreading thread-local completable-future java-17
我的代码在 Java 8 中运行良好,但是当我将其迁移到 Java 17 时,它就不起作用了。它涉及 ThreadLocal 和 CompletableFuture.runAsync。
以下是课程:
public class UriParameterHandler {
}
public class DateRangeEntity {
public String getCurrentDate(){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
return dtf.format(now);
}
}
public class SessionHandler {
private static ThreadLocal<SessionHandler> instance = new InheritableThreadLocal<>();
private UriParameterHandler uriParameterHandler;
private DateRangeEntity dateRangeEntity;
private SessionHandler() {
instance.set(this);
}
public static void initialize() {
SessionHandler handler = new SessionHandler();
handler.uriParameterHandler = new UriParameterHandler();
}
public static UriParameterHandler getUriParameterHandler() {
return instance.get().uriParameterHandler;
}
public static void setUriParameterHandler(UriParameterHandler uriParameterHandler) {
instance.get().uriParameterHandler = uriParameterHandler;
}
public static DateRangeEntity getDateRangeEntity() {
return instance.get().dateRangeEntity;
}
public static void setDateRangeEntity(DateRangeEntity dateRangeEntity) {
instance.get().dateRangeEntity = dateRangeEntity;
}
}
public class LocalThread implements Runnable{
@Override
public void run() {
if(SessionHandler.getDateRangeEntity()!=null){
System.out.println("not null");
}else{
System.out.println("is null");
}
}
}
public class SessionHandlerMain {
public static void main(String[] args) {
threadLocalDemo();
}
private static void threadLocalDemo(){
SessionHandler.initialize();
SessionHandler.setDateRangeEntity(new DateRangeEntity());
//works in java8 but not in java17
CompletableFuture.runAsync(()->{
if(SessionHandler.getDateRangeEntity()!=null){
System.out.println("not null");
}else{
System.out.println("is null");
}
}).exceptionally(e->{
e.printStackTrace();
return null;
});
/*
LocalThread localThread = new LocalThread();
localThread.run();
*/
}
}
Run Code Online (Sandbox Code Playgroud)
在threadLocalDemo()in 中SessionHandlerMain,我首先DateRangeEntity为 the 设置新对象SessionHandler,然后在runAsync()方法中调用 thegetDateRangeEntity()来检查该对象是否不为空。这在 Java 8 中有效并打印“not null”,但是当我迁移到 Java 17 时,现在会抛出此异常:
public class UriParameterHandler {
}
public class DateRangeEntity {
public String getCurrentDate(){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
return dtf.format(now);
}
}
public class SessionHandler {
private static ThreadLocal<SessionHandler> instance = new InheritableThreadLocal<>();
private UriParameterHandler uriParameterHandler;
private DateRangeEntity dateRangeEntity;
private SessionHandler() {
instance.set(this);
}
public static void initialize() {
SessionHandler handler = new SessionHandler();
handler.uriParameterHandler = new UriParameterHandler();
}
public static UriParameterHandler getUriParameterHandler() {
return instance.get().uriParameterHandler;
}
public static void setUriParameterHandler(UriParameterHandler uriParameterHandler) {
instance.get().uriParameterHandler = uriParameterHandler;
}
public static DateRangeEntity getDateRangeEntity() {
return instance.get().dateRangeEntity;
}
public static void setDateRangeEntity(DateRangeEntity dateRangeEntity) {
instance.get().dateRangeEntity = dateRangeEntity;
}
}
public class LocalThread implements Runnable{
@Override
public void run() {
if(SessionHandler.getDateRangeEntity()!=null){
System.out.println("not null");
}else{
System.out.println("is null");
}
}
}
public class SessionHandlerMain {
public static void main(String[] args) {
threadLocalDemo();
}
private static void threadLocalDemo(){
SessionHandler.initialize();
SessionHandler.setDateRangeEntity(new DateRangeEntity());
//works in java8 but not in java17
CompletableFuture.runAsync(()->{
if(SessionHandler.getDateRangeEntity()!=null){
System.out.println("not null");
}else{
System.out.println("is null");
}
}).exceptionally(e->{
e.printStackTrace();
return null;
});
/*
LocalThread localThread = new LocalThread();
localThread.run();
*/
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我将方法的逻辑移动runAsync()到扩展的类中,Runnable那么这在 Java 17 中是有效的。
有人可以告诉我为什么这种行为在 Java 17 中不同,以及是否有其他解决方法?
Seb*_*edl 11
您的代码已损坏,并且它在大多数情况下都可以工作只是偶然。
AThreadLocal提供每个线程唯一的存储位置。不同的线程,不同的存储位置。这意味着如果您在不同的线程上,您应该期望得到不同的东西ThreadLocal。
您运行代码,使用单个参数查询ThreadLocala 内部。CompletableFuture.runAsync这意味着代码将在ForkJoinPool.commonPool()执行器上运行,即在线程池中的某个线程上运行。
您绝对不应该依赖ThreadLocal此类代码中 a 内的任何值。
那么为什么代码大部分时间都能工作呢?这是因为你创建了ThreadLocal一个InheritableThreadLocal(这对于“存储服务实例”来说是一个坏主意 - 要么你的服务是线程安全的,你应该全局使用一个,要么它不是(就像你的一样)并在线程之间共享它被打破)。这个类的特殊之处在于,当创建一个新线程时, anInheritableThreadLocal被初始化为与父线程(即创建新线程的线程)相同的值,而普通的ThreadLocal只是初始化为null。
未指定何时以及如何ForkJoinPool.commonPool()初始化。最可能的策略是延迟创建,即在第一次调用方法时创建池。因此,通常,池是在您调用 时创建的CompletableFuture.runAsync(),因此池中的线程是主线程的子线程,继承您已存储在ThreadLocal.
然而,在 Java 17 中,似乎有些东西会在您初始化 之前初始化池ThreadLocal,因此它不会继承您设置的值。
然而,即使其他 Java 版本可能没有相同的行为,我仍然认为组合ForkJoinPoolandThreadLocal从根本上来说是错误的。用于ThreadLocal真正线程本地的事情并且本来就是如此。
至于LocalThread类 - 您确实意识到这只是一个run()具有在正常执行中在主线程上调用的方法的类?如果你想在另一个线程上运行它,你需要这样做new Thread(new LocalThread()).start()。但由于您显式启动了该线程,因此可以保证继承ThreadLocal此处的值,因此代码将可靠地工作。(尽管我对缓存服务的线程安全问题仍然存在。)
| 归档时间: |
|
| 查看次数: |
956 次 |
| 最近记录: |