当前位置: 首页 > 知识库问答 >
问题:

如果只有一个写线程,是否需要特殊的并发性?

糜野
2023-03-14

假设:

  1. 只有一个特定的线程设置了某个引用字段(不是长或双精度,所以写入它是原子的)
  2. 有任意数量的线程可能会读取同一个字段
  3. 稍微陈旧的读取是可以接受的(最多几秒钟)

在这种情况下,您需要挥发性或原子参考或类似的东西吗?

该条指出:

如果您严格遵守单一写入器原则,则不需要内存障碍。

这似乎表明,在我描述的情况下,你真的不需要做任何特殊的事情。

我做了一个测试,结果很奇怪:

import org.junit.Test;

public class ThreadTest {
    int onlyWrittenByMain = 0;
    int onlyWrittenByThread = 0;

    @Test
    public void testThread() throws InterruptedException {
        Thread newThread = new Thread(new Runnable() {
            @Override
            public void run() {
                do {
                    onlyWrittenByThread++;
                } while (onlyWrittenByMain < 10 || onlyWrittenByThread < 10);
                System.out.println("thread done");
            }
        });
        newThread.start();

        do {
            onlyWrittenByMain++;
            // Thread.yield();
            // System.out.println("test");
            // new Random().nextInt();
        } while (onlyWrittenByThread < 10);
        System.out.println("main done");
    }
}

有时运行this会输出“线程完成”,然后永远挂起。有时它确实完成了。所以线程可以看到主线程所做的更改,但显然main并不总是看到线程所做的更改?

如果我把系统或线程放进去。屈服,或随机调用,或使onlywritenbythread易失性,它每次都会完成(尝试了大约10次)。

这是否意味着我上面提到的博客文章是不正确的?即使在单个作者的情况下,你也必须有记忆障碍?

没有人能完全回答这个问题,所以我想我猜这可能是正确的,不需要内存屏障,但如果没有创建“先发生后发生”关系的东西,java编译器和hotspot可以进行优化(例如提升),这将使它不能做自己想要的事情。

共有3个答案

武琛
2023-03-14

这是必需的,因为读线程可能看不到读字段上的更改。你应该建立一种先发生后发生的关系。

白泽语
2023-03-14

代码中的主要问题不是CPU将做什么,而是JVM将如何处理它:变量提升的风险很高。这意味着JMM(Java内存模型)允许JVM重写:

public void run() {
    do {
        onlyWrittenByThread++;
    } while (onlyWrittenByMain < 10 || onlyWrittenByThread < 10);
    System.out.println("thread done");
}

作为另一段代码(注意局部变量):

public void run() {
    int localA = onlyWrittenByMain;
    int localB = onlyWrittenByThread;
    do {
        localB ++;
    } while (localA < 10 || localB < 10);
    System.out.println("thread done");
}

碰巧这是hotpost进行的一个相当常见的优化。在您的情况下,一旦进行了优化(可能不是在您调用该方法时立即进行,而是在几毫秒后),您在其他线程中所做的任何事情都将永远不会从该线程中可见。

卫增
2023-03-14

问题是在多核系统上缓存——如果没有像易失性这样的强制先发生关系(内存障碍之类的东西),您可以让您的写入线程写入其内核缓存中变量的副本,而您的所有读取线程读取另一个内核上变量的另一个副本。另一个问题是原子性,另一个答案解决了这个问题。

 类似资料:
  • 假设我有一组从客户机发送到服务器的请求ID。服务器的响应返回我发送的请求ID,然后我可以将其从哈希集中删除。这将以多线程的方式运行,因此多个线程可以在哈希集中添加和删除ID。然而,由于生成的ID是唯一的(从线程安全的源代码,比如现在的,它会针对每个新请求进行更新),是否需要是? 我认为这可能导致问题的唯一情况是遇到冲突,这可能需要对底层对象进行数据结构更改,但在这个用例中似乎不会发生这种情况。

  • 问题内容: 说我有一个全局对象: 有一个线程定期运行以从远程获取新编号并更新(仅写入): 并且有一个或多个线程随机使用此全局变量(仅读取): 您可以看到我不使用任何锁或对其进行保护,对吗?是否有可能引起问题的潜在问题? 更新: 就我而言,读取线程必须实时获取最新的值并不是很重要。我的意思是,如果有任何问题(由于缺少锁定/同步而导致)使一个读取线程错过了该值,那就没关系了,因为它将有机会尽快运行相同

  • 考虑以下代码: 在一次采访中,我被问到了以下问题:如果只有一个线程调用,而多个线程可以调用,那么这个代码是线程安全的吗? 我知道,如果我们有多个写入线程,竞争条件可能会发生。然而,当每个读取线程中只有一个写入线程时,我们将按顺序获取值,即我们不会在之后读取,因为的写入发生在第一次读取,因此,在同一线程中的所有后续读取。这个推理是正确的,还是我漏掉了什么?

  • 我有一张地图。假设: 多个线程正在访问此映射,但是每个线程只能访问映射中自己的条目。这意味着,如果线程T1将对象A插入到映射中,则保证没有其他线程将访问对象A。最后,线程T1也将删除对象A。 还可以保证没有线程会遍历映射。 这张地图需要同步吗?如果是,您将如何同步?(ConcurrentHashMap、Collections.synchronizedMap()或synchronized block

  • 我在datasource password字段中有特殊字符,如“(双引号)、@,~,!,%,&,},”。当我运行springboot应用程序时,当它试图连接到我遇到的数据库时- 我的问题是: (1)是否有方法查看Spring尝试连接时使用的密码? (2)是否需要转义中密码中的任何特殊字符? 我能够使用一个独立的java程序连接到数据库,但是我必须转义密码中的双引号。我在后端使用了SpringBoo

  • 类AnagramGameDefault模拟一个字谜游戏。 submitScore()应该重新计算位置,得分最高的位置为1,同一位置上可以有多个球员。 tester类: