2

我有一个用于多线程应用程序的 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 中的分配和返回操作是原子的。但是,通过使用这些锁,我不能确保每个访问者都获得最新的值(等于 using volatile)吗?

如果原语是doubleorlong怎么办?

4

6 回答 6

4

这取决于。

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

Example e = ...;

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

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

于 2011-02-16T08:56:59.497 回答
3

你在 java 中有类似 AtomicInteger 的东西,它适用于多线程应用程序。

于 2011-02-16T09:29:05.153 回答
2

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

例如,假设您有一个 32 核系统,它可以完美扩展并且运行速度比 1 核快 32 倍。然而,没有锁定的字段访问需要 1 ns,锁定需要 1 us (1000 ns),因此最终您的应用程序可能会慢约 30 倍。(慢 1000 / 快 32)如果你只有 4 个核心,它可能会慢数百倍,首先破坏了拥有多线程的目的。恕我直言。

于 2011-02-16T09:12:09.910 回答
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;
    }
}
于 2011-02-16T08:56:37.793 回答
2

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

正如前一篇文章中提到的那样,您需要了解读取和更新序列,例如 incrementI(int num) 可能会调用 getI() 和 setI() - 在这种情况下,您可以添加一个 'synchronized incrementI(int num)' 方法添加到您的示例类。然后在更高级别上完成锁定,从而减少对单独的读取和写入锁的需求,并且由于数据和行为保持在一起,因此对 OO 友好。如果您同时读取/更新多个字段,此技术将更加有用。

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

于 2011-02-16T21:46:47.123 回答
0

您不应该对原始类型、字符串(它们是不可变的)和线程安全类型(如“并发”包中的集合)使用锁。

于 2011-02-16T09:01:10.383 回答