Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 正在运行的线程能否看到私有字段中的更改?_Java_Multithreading_Concurrency - Fatal编程技术网

Java 正在运行的线程能否看到私有字段中的更改?

Java 正在运行的线程能否看到私有字段中的更改?,java,multithreading,concurrency,Java,Multithreading,Concurrency,我试图实现一种线程池,当我启动一个线程时,我自己的扩展线程的类,它的run方法将永远运行,或者直到满足某些条件 当线程运行时,它应该作为可调用接口接收工作 当它检索该值时,它应该通知一个条件变量 私有类工作线程扩展{ 私人呼叫呼叫; 私人T结果; @凌驾 公开募捐{ 试一试{ while(true){ 如果(toCall==null&&/*其他条件访问外部类字段*/){ //闲着太久 break;//正常运行完成线程 } 如果(toCall!=null&&/*其他条件访问外部类字段*/){ //

我试图实现一种线程池,当我启动一个线程时,我自己的扩展线程的类,它的run方法将永远运行,或者直到满足某些条件

  • 当线程运行时,它应该作为可调用接口接收工作
  • 当它检索该值时,它应该通知一个条件变量
  • 私有类工作线程扩展{
    私人呼叫呼叫;
    私人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
      条件发出信号
    我正在尝试实现我自己的非常简单的ThreadPoolExecutor,我的方法是拥有这些工作人员的列表,根据需要创建它们,并重用这些列表中没有做任何事情的工作人员

    如果这种方法是错误的,那么实现这种功能的最佳方法是什么

    任何帮助都将不胜感激,谢谢

    此线程是否看到toCall变量中的更改

    就可见性而言,没有任何同步,答案不一定是

    在它使用此变量并将其设置为null后,对setCallable的另一个调用是否安全

    这就是实施可能不“安全”的地方。如果两个线程同时向工作线程提交一个
    可调用的
    ,会发生什么情况?给定上述代码,它将只处理其中一个,而忽略另一个。一个更好的选择可能是让工作人员轮询一个共享的任务队列。您可以使用类似于的方法来保存任务。其中一种方法允许您在超时的情况下轮询队列

    最后,这是让线程始终运行并在需要时为其提供工作的好方法吗

    我不这么认为,因为上面提到的原因。

    正如前面所指出的,线程不一定会看到
    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的是工作线程,我试图实现的是保持许多线程处于活动状态,当我需要给它们一个工作线程时