Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/393.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 - Fatal编程技术网

Java 线程之间的可见性是否需要原子引用?

Java 线程之间的可见性是否需要原子引用?,java,multithreading,Java,Multithreading,我使用的框架在发送请求时需要回调。每个回调都必须实现这个接口。回调中的方法是异步调用的 public interface ClientCallback<RESP extends Response> { public void onSuccessResponse(RESP resp); public void onFailureResponse(FailureResponse failure); public void onError(Throwable e); }

我使用的框架在发送请求时需要回调。每个回调都必须实现这个接口。回调中的方法是异步调用的

public interface ClientCallback<RESP extends Response>
{
  public void onSuccessResponse(RESP resp);

  public void onFailureResponse(FailureResponse failure);

  public void onError(Throwable e);
}
公共接口ClientCallback
{
成功响应的公共无效(RESP RESP);
失效响应公共无效(失效响应失效);
公共无效申报人(可丢弃);
}
要使用TestNG编写集成测试,我需要一个阻塞回调。所以我使用倒计时锁存器在线程之间进行同步

这里真的需要AtomicReference还是原始引用可以吗?我知道如果使用原始引用和原始整数(而不是CountDownLatch),代码将无法工作,因为无法保证可见性。但是由于倒计时锁存器已经同步,我不确定是否需要AtomicReference的额外同步。 注意:结果类是不可变的

public class BlockingCallback<RESP extends Response> implements ClientCallback<RESP>
{
  private final AtomicReference<Result<RESP>> _result = new AtomicReference<Result<RESP>>();
  private final CountDownLatch _latch = new CountDownLatch(1);

  public void onSuccessResponse(RESP resp)
  {
    _result.set(new Result<RESP>(resp, null, null));
    _latch.countDown();
  }

  public void onFailureResponse(FailureResponse failure)
  {
    _result.set(new Result<RESP>(null, failure, null));
    _latch.countDown();
  }

  public void onError(Throwable e)
  {
    _result.set(new Result<RESP>(null, null, e));
    _latch.countDown();
  }

  public Result<RESP> getResult(final long timeout, final TimeUnit unit) throws InterruptedException, TimeoutException
  {
    if (!_latch.await(timeout, unit))
    {
      throw new TimeoutException();
    }
    return _result.get();
  }
public类BlockingCallback实现ClientCallback
{
私有最终原子引用_result=新原子引用();
专用最终倒计时闩锁_闩锁=新倒计时闩锁(1);
成功响应(RESP RESP)上的公共无效
{
_set(新结果(resp,null,null));
_倒计时();
}
失效响应上的公共无效(失效响应失效)
{
_set(新结果(null,failure,null));
_倒计时();
}
公共无效申报人(可丢弃的e)
{
_set(新结果(null,null,e));
_倒计时();
}
公共结果getResult(最终长超时,最终时间单位)抛出InterruptedException,TimeoutException
{
如果(!\u闩锁等待(超时,单位))
{
抛出新的TimeoutException();
}
返回_result.get();
}

您不需要使用其他同步对象(AtomicRefetence)这里。重点是变量是在一个线程中调用CountDownLatch之前设置的,在另一个线程中调用CountDownLatch之后读取。CountDownLatch已经执行线程同步并调用内存屏障,因此保证了写入之前和读取之后的顺序。因此,您甚至不需要使用volatile对于该字段。

为了使分配在线程之间可见,必须跨越某种内存障碍。这可以通过几种不同的方式完成,具体取决于您正试图做什么

  • 您可以使用
    volatile
    字段。对
    volatile
    字段的读取和写入是原子的,可以跨线程看到
  • 您可以使用
    原子引用
    。这是有效的,但它更灵活(您可以重新分配和传递对
    原子引用
    )的引用),并且有一些额外的操作,如
  • 您可以使用
    CountDownLatch
    或类似的类,但您需要密切注意它们提供的内存不变量。例如,
    CountDownLatch
    ,可以保证所有
    wait()
    线程将看到调用
    countDown()
    直到
    countDown()的线程中发生的一切
    被调用
  • 您可以使用
    synchronized
    块。这些块更加灵活,但需要更加小心-写入和读取必须
    同步,否则可能看不到写入
  • 您可以使用线程安全的集合,例如
    ConcurrentHashMap
    。如果您只需要跨线程引用,那么就可以使用Overkill,但对于存储多个线程需要访问的结构化数据非常有用

这并不是一个完整的选项列表,但希望您能看到有几种方法可以确保值对其他线程可见,
AtomicReference
只是其中一种机制。简而言之,这里不需要AtomicReference。不过您需要volatile

原因是您只对引用(结果)进行写入和读取,而不执行任何类似于compareAndSet()的复合操作

对于引用变量和大多数基本变量(除long和double之外的所有类型),读取和写入都是原子的。

参考, Sun Java教程

还有JLS(Java语言规范)

对引用的写入和读取始终是原子的,无论它们是作为32位值还是64位值实现。

Java 8

Java 7

Java 6

来源:

原子操作不能交错,因此可以在不担心线程干扰的情况下使用。但是,这并不能消除同步原子操作的所有需要,因为内存一致性错误仍然存在。使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会导致错误a清除与同一变量的后续读取之前发生的关系。这意味着其他线程始终可以看到对易失性变量的更改。此外,这还意味着,当线程读取易失性变量时,它不仅可以看到对易失性的最新更改,还可以看到导致该更改的代码的副作用把零钱调高

因为您只有一个写/读操作,而且它是原子操作,所以将变量设置为volatile就足够了

关于CountDownLatch的使用,它用于等待其他线程中的n个操作完成。因为您只有一个操作,所以可以使用Condition,而不是CountDownLatch

如果您对AtomicReference的使用感兴趣,您可以在实践中检查Java并发性(第326页),找到下面的书:

或者@Binita Bharti在下面的StackOverflow答案中使用的相同示例

一个好的起点是(我的重点):

内存一致性影响:在计数达到零之前,调用前线程中的操作