java - 如果我使用synchronized,我应该使用 volatile 吗?

标签 java

如果我使用synchronized处理某些可变状态,我每次都应该使用 volatile 吗?

  1. 同步使我(状态/线程)安全
  2. volatile 使线程更新关于共享可变状态

如果我关心要更新的线程,那么我应该在任何地方都放置 volatile

编辑:有两个用例:

1.

1000 个线程读取和写入此对象(它们希望更新状态 a):

 class A {
   private int a;
   public synchronized int getA() {...}
   public void setA(int a) {...}
 }

2.

ThreadA 有 1000 个线程。他们希望更新状态 a

class ThreadA extends Thread {
 private int a;
 public void run() { synchronized(a) { ... } }
}

最佳答案

与许多性能问题一样,真正的问题是简单性和清晰度。我建议使用同步或 volatile ,因为使用两者可能会造成混淆。两者同时使用是多余的,因此效率稍低,但不太可能足够重要。我会更担心如何使代码尽可能易于理解,并且不会做超出您需要的事情。

在第一种情况下,只有 volatile 才有意义(或一致使用同步)

class A {
   private volatile int a;
   public int getA() {...}
   public void setA(int a) {...}
 }

在第二种情况下,同步本地对象是没有意义的,您可以将其删除。我也不会扩展 Thread,这是不好的做法。

虽然您可能有 1000 个线程,但您可能只拥有 8-16 个 CPU,但拥有如此多的 CPU 绑定(bind)线程并不是一个好主意。减少线程数量,您可能会通过减少开销来提高性能。

您应该将它们设计得尽可能独立,因为如果不能,单个线程可能会更快,因为它不会产生缓存一致性开销。

<小时/>

恕我直言,使用枚举比使用 Guava MemorizeSupplier 更简单,但速度更快

public class GuavaMain {
    interface AAA {
        int hashCode();
    }

    enum Singleton implements AAA {
        INSTANCE
    }

    public static void main(String... ignored) {
        Supplier<AAA> memoize = Suppliers.memoize(new Supplier<AAA>() {
            @Override
            public AAA get() {
                return new AAA() {
                };
            }
        });

        for (int j = 0; j < 10; j++) {
            int runs = 5000;
            long time1 = System.nanoTime();
            for (int i = 0; i < runs; i++) {
                // call a method on out lazy instance
                Singleton.INSTANCE.hashCode();
            }
            long time2 = System.nanoTime();
            for (int i = 0; i < runs; i++) {
                // call a method on out lazy instance
                memoize.get().hashCode();
            }
            long time3 = System.nanoTime();
            System.out.printf("enum took %,d ns and memorize took %,d ns avg%n",
                    (time2 - time1) / runs, (time3 - time2) / runs);
        }
    }
}

打印

enum took 179 ns and memorize took 301 ns avg
enum took 74 ns and memorize took 97 ns avg
enum took 62 ns and memorize took 175 ns avg
enum took 58 ns and memorize took 146 ns avg
enum took 58 ns and memorize took 147 ns avg
enum took 56 ns and memorize took 111 ns avg
enum took 36 ns and memorize took 86 ns avg
enum took 36 ns and memorize took 84 ns avg
enum took 36 ns and memorize took 82 ns avg
enum took 36 ns and memorize took 82 ns avg

关于java - 如果我使用synchronized,我应该使用 volatile 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16719004/

相关文章:

java - 安装适用于 Java 的 openCSV。我该如何处理 .gz 文件?

Java:如何从另一个方法输出用户输入(多个字符串/整数)

java - CheckBoxTableCell 更改监听器不工作

java - Java中的紧凑混洗代码

java - 组织.apache.jasper.JasperException : Cannot find any information on property 'sName' in a bean of type 'AddUserBean'

java - 属性 "Any"已定义。使用 <jaxb :property> to resolve this conflict

java - 在 Java 中设置对话框窗口大小的推荐方法是什么

java - Maven项目jar库如何获取到另一个项目

java - 如何实现同级项目之间的 Maven 源依赖?

java - 使用 Java 从文件转换 JSON 对象