java 当可以接受陈旧值时,volatile是否是必需的?

vojdkbi0  于 2023-05-27  发布在  Java
关注(0)|答案(2)|浏览(88)

我有一个微服务,它可以接收真实的的HTTP请求。这些请求对共享对象执行读取。
此服务还具有更新共享对象的引用的后台作业。
我知道在race中,一些请求线程可能会看到整个请求的陈旧数据,因为数据可能是从本地缓存中提取的。我的问题是,如果我可以接受几秒钟的陈旧数据,我是否需要使这个共享对象变得不稳定?

nbysray5

nbysray51#

是的。否则不能保证更新后的值将 * 永远 * 被其他线程看到。

vlju58qv

vlju58qv2#

因此,您有一个线程(“后台作业”),它定期构造一个新对象,然后在全局变量中存储对它的引用。
问题是,您需要担心的不仅仅是全局变量的“过时”。共享对象的每个单独字段可以独立于每个其他字段是“陈旧的”。每个子对象的每个部分可以独立于任何其他部分单独地“陈旧”。
如果共享对象比单个共享Integer更复杂,则“后台作业”必须逐段构造它。而且,由于线程之间没有同步,查看该对象的任何其他线程都可能看到该对象的某些部分“已构建”,而其他部分似乎尚未构建。
如果没有同步,则不能保证客户端线程看到对象的字段按照后台作业执行分配的相同顺序更新。较早的分配可能会通过,而较晚的分配似乎仍然是“陈旧的”。
假设你有这个:

class SharedObj {
    public int x;
    public int y;

    public static SharedObj globalRef = new SharedObj();
}

假设后台线程这样做:

SharedObj localRef = new SharedObj();
localRef.x = 3;
localRef.y = 5;
SharedObj.globalRef = localRef;

最后,在其他线程中:

SharedObj localRef = SharedObj.globalRef;
if (localRef.y < localRef.x) {
    System.out.println("Boo!!");
}

其他线程可以打印“Boo!!"?是的。它可以。我不知道所有的可能性,但有一种可能性是,它可以看到x==3y==0
你可以使用volatile来保证程序永远不会打印“Boo!!":

static volatile SharedObj globalRef = new SharedObj();

它之所以有效,是因为volatile的读写操作在两个线程之间建立了“happenbefore”关系。volatile关键字保证了“后台作业”在更新globalRef之前所做的一切在其他线程从globalRef读取新引用时必须对其他线程可见。

相关问题