-3

我最近参加了一次采访,其中有一个问题被问到“是否可以改进以下代码”:

public class Performance{
    static class C1 {
        volatile long c1;
        volatile long c2;
    }

    static C1 p = new C1();

    static class Worker implements Runnable {

        private static final int INT = Integer.MAX_VALUE / 8;
        private final boolean b;

        Worker(boolean b) {
            this.b = b;
        }

        @Override
        public void run() {
            if (b) {
                for (int i = 0; i < INT; i++) {
                    p.c1++;
                }
            } else {
                for (int i = 0; i < INT; i++) {
                    p.c2++;
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Worker(true));
        Thread t2 = new Thread(new Worker(false));
        t1.start();
        t2.start();
    }

}
4

3 回答 3

4

java 7 的 JIT 编译器非常聪明。它删除或重新排序未使用的字段。为了避免虚假分享,您应该添加volatile关键字。

Java 8 有新的注解@sun.misc.Contented。有关详细信息,请参阅http://shipilev.net/talks/jvmls-July2013-contended.pdf

于 2013-10-17T10:27:54.637 回答
1

填充变量可能是一种改进,但由于(理论上)您的代码最终在哪个平台上运行是未知的,所以您不能确定填充是否能解决问题。有些处理器将从中受益,如果您这样做,有些处理器会减慢速度。

这是一个如此深入的微优化,很容易破坏而不是修复。我会把它留给 JVM 来决定它是否想要这样做,除非这是在一个完美定义的环境中绝对必须具备的。

顺便提一句。上面该代码的最优化版本:

static class C1 {
    final long c1 = Integer.MAX_VALUE >>> 3;
    final long c2 = c2;
}

;)

于 2013-10-17T10:38:09.450 回答
0

我的回答是,是的,可以通过使用可变填充来避免缓存争用。Java 8 LongAdder 类中介绍了这种技术。 填充是一种通过尝试确保不同的单元不会落在同一缓存行中来减少 CPU 缓存争用的策略。

public class Performance{
    static class C1 {
        volatile long c1;
        long q1, q2, q3, q4, q5, q6, q7, q8;
        volatile long c2;
    }

    static C1 p = new C1();

    static class Worker implements Runnable {

        private static final int INT = Integer.MAX_VALUE / 8;
        private final boolean b;

        Worker(boolean b) {
            this.b = b;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            if (b) {
                for (int i = 0; i < INT; i++) {
                    p.c1++;
                }
            } else {
                for (int i = 0; i < INT; i++) {
                    p.c2++;
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("took: " + (end - start) + " " + p.c1 + p.c2);
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Worker(true));
        Thread t2 = new Thread(new Worker(false));
        t1.start();
        t2.start();
    }

}
于 2013-10-17T10:11:46.867 回答