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

时间:2013-05-23 16:16:51

标签: java

如果我使用volatile处理某些synchronized,我每次都应该使用mutable state

  1. synchronized让我(州/线程)安全
  2. volatile让线程更新shared mutable state
  3. 然后我应该把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) { ... } }
    }
    

2 个答案:

答案 0 :(得分:0)

与许多性能问题一样,真正的问题是简单明了。我建议使用synchronized或volatile,因为使用两者可能会令人困惑。使用两者都是多余的,因此效率稍低,但不太可能重要。我更担心的是让代码尽可能易于理解,并且只需要做你想做的事情。

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

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

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

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

你应该将它们设计得尽可能独立,因为如果你不能,那么单个线程可能会更快,因为它不会有高速缓存的一致性。


恕我直言使用枚举比使用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

答案 1 :(得分:-1)

如果你在任何地方使用volatile,你将在代码中引入很多同步点,这会降低效率。

编码并发时,最好的选择是将共享可变性分成小部分,甚至尽可能地消除共享状态。