我有一个在多线程应用程序中使用的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
)?
如果基元是double
或long
?
答案 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”包中的集合)。