标签: java-memory-model

同步数据读/写到/从主存储器

当一个同步方法完成时,它是只把它修改的数据推送到主存,还是所有的成员变量,类似地,同步方法执行时,它是从主存中只读取它需要的数据还是清除所有的数据?缓存中的成员变量并从主内存中读取它们的值?例如

public class SharedData
{

    int a; int b; int c; int d;

    public SharedData()
    {
        a = b = c = d = 10;
    }

    public synchronized void compute()
    {
        a = b * 20;
        b = a + 10;
    }

    public synchronized int getResult()
    {
        return b*c;
    }

}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,假设计算由线程 A 执行,而 getResult 由线程 B 执行。执行compute后,线程A会用a和b更新主存还是会更新a、b、c和d。在执行 getResult 之前,threadB 将只从主内存中获取 b 和 c 的值,还是会清除缓存并获取所有成员变量 a、b、c 和 d 的值?

java multithreading synchronization caching java-memory-model

5
推荐指数
1
解决办法
2027
查看次数

Java中的内存分配 - Android

如果我有:

Bitmap bitmap = Bitmap.create(..); // instance a
bitmap = Bitmap.create(...); // instance b
bitmap = null;
bitmap = Bitmap.create(...); // instance c
bitmap.recycle();
bitmap = Bitmap.create(...); // instance d
bitmap.recycle();
bitmap = null;
Run Code Online (Sandbox Code Playgroud)

执行此代码后,4个实例中的哪些仍在内存中?我知道.recycle()指示本机代码将所有资源解除分配给该对象,但不能保证何时发生这种情况.

我问的原因是让我们看看以下循环:

Bitmap bitmap = null;

while (true) {
    bitmap = Bitmap.create(...);    
}
Run Code Online (Sandbox Code Playgroud)

我假设这最终会崩溃应用程序(内存不足)?如果是这样,该循环应该如何重写?(如果我使用位图来设置动画并显示更改的状态).

java android garbage-collection java-memory-model

5
推荐指数
1
解决办法
682
查看次数

同步是否保证一个线程会看到另一个线程正在修改的非易失性变量的最新值?

这是一个简单的例子:

private long counter = 0;

// note this method is NOT synchronized
// this will be called by thread A
public void increment() { counter++; }

// note this method IS synchronized
// this will be called by thread B
public synchronized long value() { return counter; }
Run Code Online (Sandbox Code Playgroud)

所以我只想获得一个好的值counter,而不是 cpu 缓存中的卡住值,因为该变量是非易失性的。目标是不使计数器可变,因此它不会影响执行增量的线程 A,但仅影响线程 B,我不在乎,当它读取变量时。

只是为了记录,我计划counter在线程 A 已经完成时从线程 B 中读取 的值......

java concurrency multithreading real-time java-memory-model

5
推荐指数
1
解决办法
1500
查看次数

CompareAndSet 不成功操作的内存影响

Java通过其原子类公开CAS操作,例如

boolean compareAndSet(expected,update)
Run Code Online (Sandbox Code Playgroud)

JavaDocs指定了compareAndSet 操作的内存效应,如下所示

CompareAndSet 和所有其他读取和更新操作(例如 getAndIncrement)都具有读取和写入 易失性变量的记忆效应。

这绝对适用于成功的compareAndSet调用。但是如果compareAndSet返回,记忆效应也成立吗false

我想说,不成功compareAndSet对应于易失性读取(因为在这种情况下必须访问原子实例的当前值),但我不明白为什么 CAS 应该在不成功的情况下执行特殊的内存屏障指令。

问题实际上是,不成功的 CAS 是否也建立了happens-before关系。考虑以下程序:

public class Atomics {
    private static AtomicInteger ai = new AtomicInteger(5);
    private static int x = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (x == 0) {
                ai.compareAndSet(0, 0); // returns false
            }
        }, "T1").start();

        new Thread(() -> {
            x = 1;
            ai.compareAndSet(0, 0); // returns false
        }, "T2").start();
    }
}
Run Code Online (Sandbox Code Playgroud)

线程T2(和程序)肯定会终止吗?

java concurrency java-memory-model compare-and-swap

5
推荐指数
1
解决办法
440
查看次数

非易失性字段+来自另一个线程的第一个对象访问(java)

我已经在某个服务器类型的应用程序上工作了一段时间,我发现它的设计挑战了我在 Java 中看待内存一致性(可以这么说)的方式。

\n\n
\n\n

该应用程序使用 NIO,因此 I/O 线程数量有限(它们只执行网络 I/O,不执行其他操作;它们永远不会终止,但可能会被阻塞以等待更多工作)。

\n\n

ClientCon每个连接在内部都表示为特定类型的对象,在本示例中我们称其为特定类型的对象。ClientCon有各种与会话相关的字段,这些字段都不是易失性的。不存在与获取/设置这些字段的值相关的任何类型的同步。

\n\n

接收到的数据由具有固定最大大小的逻辑单元组成。每个这样的单元都有一些允许决定处理类型(类)的元数据。一旦完成,就会创建该类型的新对象。所有此类处理程序都有字段,但这些字段都不是易失性的。然后,I/O 线程(为每个线程分配一个具体的 I/O 线程ClientConprotected在新处理程序对象上调用剩余缓冲区内容(在读取元数据之后)的 read 方法。

\n\n

之后,相同的处理程序对象被放入一个特殊的队列中,然后将该队列(队列)提交给线程池执行(其中调用每个处理程序的 run 方法以根据读取的数据采取操作)。对于这个例子,我们可以说 TP 线程永远不会终止

\n\n

因此,TP 线程将获得它以前从未访问过的对象。该对象的所有字段都是非易失性的(并且大多数/全部都是非最终的,因为它们是在构造函数外部修改的)。

\n\n

处理程序的运行方法可以基于会话特定字段进行操作,ClientCon也可以设置它们和/或对处理程序对象自己的字段进行操作,这些字段的值在读取方法中设置。

\n\n
\n\n

根据 CPJ(Java 并发编程:设计和原理):

\n\n
\n

线程第一次访问对象的字段时,它会看到该字段的初始值或自其他线程写入以来的值。

\n
\n\n

该引用的更全面的示例可以在JLS 17.5中找到:

\n\n
class FinalFieldExample { \n    final int x;\n    int y; \n    static FinalFieldExample f;\n\n    public FinalFieldExample() {\n        x = 3; \n …
Run Code Online (Sandbox Code Playgroud)

java multithreading java-memory-model jls

5
推荐指数
1
解决办法
872
查看次数

关于重新排序:尽管使用volatile,为什么此代码会抛出RuntimeException?

public class ReOrdering implements Runnable {
    int one, two, three, four, five, six;
    volatile int volaTile;

    @Override
    public void run() {
        one = 1;
        two = 2;
        three = 3;
        volaTile = 92;
        int x = four;
        int y = five;
        int z = six;
    }
}
Run Code Online (Sandbox Code Playgroud)

的任务one,two并且three可以重新排序,只要他们所有发生之前volatile写.同样,x,yz 作为陈述可能被重新排序volatile写个个之前发生.该volatile操作通常称为内存屏障.保证之前发生的事情确保volatile变量的读写指令 不能在内存屏障上重新排序.

在保证具有另一种效果之前发生:当线程写入volatile变量时,在写入volatile 变量之前由线程更改的所有其他变量(包括非易失性)也会刷新到主存储器.当线程读取 volatile变量时,它还会读取所有其他变量(包括非易失性),这些 …

java multithreading java-memory-model

5
推荐指数
1
解决办法
83
查看次数

什么是“同步障碍”?

最近我正在阅读Doug Lea 关于 JSR 133: JavaTM Memory Model and Thread Specification Revision的页面The JSR-133 Cookbook for Compiler Writers

我在那里读到这一行:

内存屏障本身并不是“同步屏障”

我搜索了一些关于内存屏障同步屏障之间差异的资源,但找不到任何好的东西。我也无法判断同步障碍与 Java 与其他语言相比是否有任何不同。

java jvm synchronized java-memory-model memory-barriers

5
推荐指数
1
解决办法
154
查看次数

如何理解JDK9内存模型?

我正在学习 JDK9 内存模型。

在观看Java Memory Model Unlearning Experience演讲 并阅读 Using JDK 9 Memory Order Modes 一文后

我对一些概念感到困惑。

  1. 不透明是否立即保证可见性

  2. 如何理解论文中的偏序全序

对于第一个问题,论文说

使用等待变量值的空自旋几乎从来都不是一个好主意。使用 Thread.onSpinWait、Thread.yield 和/或阻塞同步来更好地应对“最终”可能需要很长时间的事实,尤其是当系统上的线程数多于内核数时。

所以如果我写代码:

// shared variable I and VarHandle I_HANDLE which referred to I
public static int I = 0;

public static final VarHandle I_HANDLE;

// Thread-1
I_HANDLE.setOpaque(1);

// Thread-2
while((int) I_HANDLE.getOpaque() == 0){
}
Run Code Online (Sandbox Code Playgroud)

线程 2 最终终止,但可能会在很长时间后终止?

如果是这样,是否有任何最小方法可以保证线程 2 立即看到线程 1 的修改?(发布/获取?易变?)

java multithreading volatile java-memory-model java-9

5
推荐指数
1
解决办法
267
查看次数

java8中使用volatile实现Acquire/Release模型

就我而言,我想Acquire/Release在 java8 中使用volatile.

所以我写的代码使用了一个 volatile 共享变量I来保证MAP其他线程可以看到的修改。

public static volatile int I = 0;

public static final Map<String, String> MAP = new HashMap<>();

// run at Thread-1
public static void write(){
    MAP.put("test", "test");
    I++;                      // release
}

// run at Thead-2
public static void read(){
    int i = I;                // acquire
    MAP.get("test");          // want to see the modifying by write()
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 代码同步是否正确?
  2. 有没有可能是 JIT 消除了未使用的局部变量,i从而导致acquire操作无效?

java jit volatile java-memory-model java-8

5
推荐指数
1
解决办法
121
查看次数

了解上下文切换期间的 Java 内存模型

最近在一次会议演讲中,使用以下示例来演示多线程环境中的 Java 内存模型。

public class A {

    public static boolean done;

    public static void main(String[] args) throws InterruptedException {
        done = false;
        new Thread(new Runnable(){
            public void run() {
                System.out.println("running...");

                int count = 0;
                while (!done) {
                    count++;
                }

                System.out.println("Exiting thread");
            }
        }).start();

        System.out.println("in main...");
        Thread.sleep(2000);

        System.out.println("setting done to true");
        done = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道上面代码中创建的新线程永远不会退出,因为done变量被缓存在线程的本地缓存中。一个适当的解决方案是使done变量可变。

但是如果在while循环中,我们调用Thread.sleep()如下

    while (!done) {
        count++;
        try {Thread.sleep(0);} catch(Exception e){}
    }
Run Code Online (Sandbox Code Playgroud)

然后线程成功退出。

我的理解是,由于sleep(0)会发生上下文切换,这将使缓存条目无效,因此每次done检索的更新值。我的理解正确吗?这种行为也取决于机器的核心数吗?

java multithreading java-memory-model

5
推荐指数
1
解决办法
75
查看次数