Java 正在运行的线程能否看到私有字段中的更改?
我试图实现一种线程池,当我启动一个线程时,我自己的扩展线程的类,它的run方法将永远运行,或者直到满足某些条件Java 正在运行的线程能否看到私有字段中的更改?,java,multithreading,concurrency,Java,Multithreading,Concurrency,我试图实现一种线程池,当我启动一个线程时,我自己的扩展线程的类,它的run方法将永远运行,或者直到满足某些条件 当线程运行时,它应该作为可调用接口接收工作 当它检索该值时,它应该通知一个条件变量 私有类工作线程扩展{ 私人呼叫呼叫; 私人T结果; @凌驾 公开募捐{ 试一试{ while(true){ 如果(toCall==null&&/*其他条件访问外部类字段*/){ //闲着太久 break;//正常运行完成线程 } 如果(toCall!=null&&/*其他条件访问外部类字段*/){ //
私有类工作线程扩展{
私人呼叫呼叫;
私人T结果;
@凌驾
公开募捐{
试一试{
while(true){
如果(toCall==null&&/*其他条件访问外部类字段*/){
//闲着太久
break;//正常运行完成线程
}
如果(toCall!=null&&/*其他条件访问外部类字段*/){
//此线程运行后调用setCallable时是否达到此块?
结果=toCall.call();
hasResult.signal();//条件变量
toCall=null;
}
}
}捕获(中断异常ie){
//待办事项
}
}
public void setCallable(Callable toCall){
this.toCall=toCall;
}
public void getResult(){
返回结果;
}
}
我的主要疑虑是:
- 此线程是否看到
变量中的更改toCall
- 在使用此变量并将其设置为null后,对
的另一个调用是否安全setCallable
- 最后,这是让线程始终运行并在需要时为其提供工作的好方法吗?发送工作的线程必须等待
条件发出信号hasResult
可调用的
,会发生什么情况?给定上述代码,它将只处理其中一个,而忽略另一个。一个更好的选择可能是让工作人员轮询一个共享的任务队列。您可以使用类似于的方法来保存任务。其中一种方法允许您在超时的情况下轮询队列
最后,这是让线程始终运行并在需要时为其提供工作的好方法吗
我不这么认为,因为上面提到的原因。正如前面所指出的,线程不一定会看到toCall
变量的变化
将变量声明为volatile是确保这一点安全的一种方法,但是(在不深入研究Java内存模型细节的情况下),这可能不是必需的。更准确地说:这里的可能不够,因为显然存在一个涉及toCall
变量的竞争条件:
| Worker thread: | External thread:
---| ------------------------------------------|--------------------------------
0: | while(true) { |
1: | | worker.setCallable(c);
2: | if(toCall != null){ |
3: | | worker.setCallable(null);
4: | result = toCall.call(); // CRASH! |
5: |
因此,外部线程可以将toCall
变量设置为非null
值。工作线程可能会发现toCall=null,并输入if
-语句的主体。此时,外部线程可能会将toCall
设置为null
,从而在工作线程尝试执行toCall.call()时导致null点异常
当然,这可以通过将对toCall
变量的访问包装到同步块中来解决。然后,它就不再是易变的了,这就是我上面提到的
您的代码可能存在其他一些同步问题。您没有向我们提供所有详细信息,特别是关于hasResult
变量的角色。你没有说你想自己实现这一点是否有特定的原因
但总的来说:
有更简单、更优雅、更惯用的方法来实现您可能想要的目标。其中一条注释已经将您指向该类,该类提供了您似乎想要在那里实现的功能。根据同步发生的位置和方式(即在本例中的等待),有不同的选项。最简单的可能是executor服务:
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<T> future = executorService.submit(toCall);
// Whereever this should be consumed:
T t = future.get(); // Will wait until the result is computed
ExecutorService ExecutorService=Executors.newFixedThreadPool(1);
Future=executorService.submit(toCall);
//无论在何处使用:
T T=future.get();//将等待直到计算结果
或者,该类提供了添加“回调”的机制,当结果可用时,将通知该“回调” 我建议您阅读更多有关Futures的内容,它可以帮助您始终在线程中运行,主线程是为您创建的,但它只是一个线程,与其他线程一样,如果您创建一个新线程,它的工作原理也是一样的。“为什么不呢?”因为变量不是易变的,而且它不是在同步块中读写的。@AndyTurner你是对的(我已经考虑过了)。然而,目前还不清楚OP是否质疑可见性。标题谈到了“私人”,所以我更多地考虑了可访问性。我将尝试在编辑中澄清。我了解setCallable方法可能发生的争用条件,但它只传递非null变量,因此唯一可以将toCall变量设置为null的是工作线程,我试图实现的是保持许多线程处于活动状态,当我需要给它们一个工作线程时