java内存模型和内存结构_Java内存模型和优化

java内存模型和内存结构

总览

许多多线程代码开发人员都熟悉这样的想法,即不同的线程可以对持有的值有不同的看法,这不是唯一的原因,即如果线程不安全,它可能不会看到更改。 JIT本身可以发挥作用。

为什么不同的线程看到不同的值?

当您有多个线程时,它们将尝试例如通过尝试访问同一内存来最小化它们将交互的数量。 为此,他们有一个单独的

本地副本,例如在1级缓存中。 该缓存通常最终是一致的。 我已经看到了两个线程看到不同值的短时间,范围在1微秒到10毫秒之间。 最终,线程被上下文切换,缓存被清除或更新。 无法保证何时会发生这种情况,但是几乎总是不到一秒钟。

JIT如何发挥作用?

Java内存模型表示无法保证不是线程安全的字段将看到更新。 这允许JIT进行优化,将仅读取而不写入的值有效地内联到代码中。 这意味着即使更新了缓存,更改也可能不会反映在代码中。

一个例子

该代码将一直运行,直到将布尔值设置为false为止。

>static class MyTask implements Runnable {
    private final int loopTimes;
    private boolean running = true;
    boolean stopped = false;

    public MyTask(int loopTimes) {
        this.loopTimes = loopTimes;
    }

    @Override
    public void run() {
        try {
            while (running) {
                longCalculation();
            }
        } finally {
            stopped = true;
        }
    }

    private void longCalculation() {
        for (int i = 1; i < loopTimes; i++)
            if (Math.log10(i) < 0)
                throw new AssertionError();
    }
}

public static void main(String... args) throws InterruptedException {
    int loopTimes = Integer.parseInt(args[0]);
    MyTask task = new MyTask(loopTimes);
    Thread thread = new Thread(task);
    thread.setDaemon(true);
    thread.start();
    TimeUnit.MILLISECONDS.sleep(100);
    task.running = false;
    for (int i = 0; i < 200; i++) {
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.println("stopped = " + task.stopped);
        if (task.stopped)
            break;
    }
}

该代码反复执行一些对内存没有影响的工作。 唯一的区别是需要多长时间。 通过花费更长的时间,它将确定在运行之前或之后将run()中的代码优化为false。

如果我用10或100和-XX:+ PrintCompilation来运行,我会看到

111    1     java.lang.String::hashCode (55 bytes)
112    2     java.lang.String::charAt (29 bytes)
135    3     vanilla.java.perfeg.threads.OptimisationMain$MyTask :longCalculation (35 bytes)
204    1 % ! vanilla.java.perfeg.threads.OptimisationMain$MyTask :run @ 0 (31 bytes)
stopped = false
stopped = false
stopped = false
stopped = false
... many deleted ...
stopped = false
stopped = false
stopped = false
stopped = false
stopped = false

如果我用1000运行它,您会看到run()尚未编译并且线程停止

112    1     java.lang.String::hashCode (55 bytes)
112    2     java.lang.String::charAt (29 bytes)
133    3     vanilla.java.perfeg.threads.OptimisationMain $MyTask::longCalculation (35 bytes)
135    1 %   vanilla.java.perfeg.threads.OptimisationMain $MyTask::longCalculation @ 2 (35 bytes)
stopped = true

一旦线程被编译,即使线程将进行多次上下文切换等,也永远不会看到更改。

如何解决这个问题

简单的解决方案是使该字段易变。 这将确保该字段的值是一致的,而不仅仅是最终一致,这可能是缓存可能为您执行的操作。

结论

虽然有很多类似的问题示例; 为什么我的线程没有停止? 答案更多与Java内存模型有关,Java内存模型允许JIT“内联”它执行硬件的字段,并在不同的缓存中具有多个数据副本。

参考: Vanilla Java博客上的JCG合作伙伴 Peter Lawrey提供的Java内存模型和优化

翻译自: https://www.javacodegeeks.com/2013/01/java-memory-model-and-optimisation-2.html

java内存模型和内存结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值