在多线程应用程序中是否应使用ReadWriteLock锁定原始类型的getter / setter?

时间:2011-02-16 08:45:03

标签: java multithreading synchronization locking readwritelock

我有一个在多线程应用程序中使用的Java类。很可能同时访问。多个并发读操作不应该阻塞,因此我使用的是ReadWrite锁。

class Example {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private int i;
    private boolean b;

    public int getI() {
      lock.readLock().lock();
      final int tmp = i;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setI( final int i ) {
      lock.writeLock().lock();
      this.i = i;
      lock.writeLock().unlock();
    }

    public boolean getB() {
      lock.readLock().lock();
      final boolean tmp = b;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setB( final boolean b ) {
      lock.writeLock().lock();
      this.b = b;
      lock.writeLock().unlock();
    }
}

为简单起见,我在本例中省略了锁周围的try...finally块。

我想知道是否有必要(或者说推荐)锁定/同步原始类型的getter和setter?我知道Java中的赋值和返回操作是原子的。但是,通过使用这些锁,我不确保每个访问者都获得最新值(等于使用volatile)?

如果基元是doublelong

,该怎么办?

6 个答案:

答案 0 :(得分:4)

这取决于。

但是,请注意,通常需要以更粗粒度的级别同步操作,例如:

Example e = ...;

synchronized (e) {
    e.setI(e.getI() + 10);
}

对于这种情况,您的内部锁是多余的。因此,在使用这些对象而不是内部同步时应用外部同步可能会更好。

答案 1 :(得分:3)

你在java中有类似AtomicInteger的东西,它适用于MultiThreaded Application。

答案 2 :(得分:2)

问问自己这个类是否可以实现为不可变类。它将更容易使用,并且本质上是线程安全的。您不必担心并发,锁定,同步等。

不可变类的示例:

final class Example {
    private final int i;
    private final boolean b;

    public Example(int i, boolean b){
        this.i = i ;
        this.b = b;
    }

    public int getI() {
        return i;
    }

    public boolean getB() {
        return b;
    }
}

答案 3 :(得分:2)

我会设计你的应用程序,这样你就不会同时访问像这样的原始数据类型。添加这样的低级别锁定可能会使应用程序变慢,因此不值得对应用程序进行多线程处理。

e.g。假设您拥有一个32核心系统,可以完美扩展,运行速度比1核心快32倍。但是,没有锁定的字段访问需要1 ns,锁定需要1 us(1000 ns),因此最终您的应用程序可能会慢大约30倍。 (1000慢/ 32快)如果你只有4个核心,它可能会慢几百倍,从根本上打败多线程的目的。 IMHO。

答案 4 :(得分:2)

没有必要锁定/同步原始类型的getter和setter - 在大多数情况下将它们标记为volatile是足够的(除了你提到的double和long之外)

正如前面提到的帖子之一,您需要了解读取和更新序列,例如incrementI(int num),它可能会调用getI()和setI() - 在这种情况下,您可以添加'synchronized incrementI(int num)'方法到Example类。然后在更高级别完成锁定,减少了对单独的读写锁定的需求,并且由于数据和行为保持在一起,因此非常友好。如果您一次读取/更新多个字段,此技术将更加有用。

虽然如果您只是一次读取/写入/更新一个字段,那么AtomicXX类更合适

答案 5 :(得分:0)

你不应该使用lock来处理原始类型,String(它们是不可变的)和线程安全的类型(比如“concurrent”包中的集合)。