首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果我只需要保证“发生前”关系,而不是原子性,那么应该使用并发类还是同步块?

如果我只需要保证“发生前”关系,而不是原子性,那么应该使用并发类还是同步块?
EN

Stack Overflow用户
提问于 2013-10-12 23:46:02
回答 4查看 275关注 0票数 0

我正在开发一个池机制。池中的对象将由不同的线程使用。因此,我需要保证在访问这些对象的非最终属性时会有一个happens-before关系。

由于给定的对象一次只被一个线程访问,所以我不需要保证任何原子性。当只需要实现happens-before时,还有什么更优化的?每次读取或修改一个非最终属性时,都要在最终属性上使用同步块吗?或使用并发类,如ConcurrentMap

(我问这个问题并不是问池本身的属性,它显然需要提供原子性;我只是问从池中获得的对象的属性)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-10-13 00:48:10

每次读取或修改一个非最终属性时,都要在最终属性上使用同步块吗?还是使用并发类,如ConcurrentMap?

Synchronization是围绕一个名为内在锁或监视器锁的内部实体构建的。每个对象都有一个与其相关联的内在锁。按照约定,需要对对象的字段进行独占和一致访问的线程必须在访问对象字段之前获取对象的内部锁,然后在访问对象字段时释放内部锁。

ConcurrentMap的一个实现就是,ConcurrentHashMap使用可重入锁,它是互斥锁,锁由lock()方法获取,线程保存到调用unlock()方法。尽管synchronized keyword,提供了与隐式锁一样的可见性和顺序保证,但ReentrantLock提供了更多的功能,并在某些方面有所不同:

  1. 在发生争用的情况下,可以通过指定ReentrantLock来为等待时间最长的线程提供锁,从而使fairness property变得公平。
  2. 提供方便的tryLock()方法来获取锁,只有当它可用或不被任何其他线程所持有时,才能减少等待锁的线程阻塞。如果在特定时间段内无法使用锁,则可以使用带超时的tryLock()来超时。
  3. synchronized keyword,的情况下,线程可能会被阻塞,等待锁的时间不确定,并且无法控制它。ReentrantLock提供了一个名为lockInterruptibly()的方法,它可以用于在等待锁定时中断线程。

使用reentrant lock的示例可以从ConcurrentHashMap的内部replace(K key, V oldValue, V newValue)函数实现中显示:

代码语言:javascript
复制
boolean replace(K key, int hash, V oldValue, V newValue) { 
               // called by replace(K key, V oldValue, V newValue)
        lock(); // acquire the lock
        try {
            HashEntry<K,V> e = getFirst(hash);
            while (e != null && (e.hash != hash || !key.equals(e.key)))
                e = e.next;

            boolean replaced = false;
            if (e != null && oldValue.equals(e.value)) {
                replaced = true;
                e.value = newValue;
            }
            return replaced;
        } finally {
            unlock(); // unlock 
        }
    }

还有其他一些函数,如put()writeObject(java.io.ObjectOutputStream)等,也是使用使用ReentrantLock可重入同步来实现的,没有这些功能,同步代码将不得不采取许多额外的预防措施,以避免线程导致自身阻塞。这就是为什么我认为就你的情况而言,ConcurentMap更可取。

参考:

  1. 内禀锁与同步
  2. ReentrantLock类
  3. 同步与ReentrantLock的区别
  4. ConcurrentHashMap
票数 3
EN

Stack Overflow用户

发布于 2013-10-14 07:00:57

由于给定的对象一次只被一个线程访问,所以我不需要保证任何原子性。

如果我做对了,您的意思是您的对象将通过线程共享,但实际上没有并发性(对象永远不会被多个线程并发访问)。您想知道需要什么同步。

我是也想知道,我的结论是:如果对象Y同步访问对象X,那么对象X本身就不需要同步。在池的情况下,您可能使用同步的方法获得和释放对象,因此它们保护池中的对象。

让我们考虑池中的对象O。池有两个方法Object acquire()release(Object o)是同步的。要将对象O从线程T1传递给T2,T1必须首先释放O,然后T2必须获得O。已经发生了--在T1的发布与T2的获取之间的关系之前,因此对象O不需要自身同步。

票数 2
EN

Stack Overflow用户

发布于 2013-10-13 00:16:44

如果您需要发生的字段--在关系开始之前是一个原语,并且您使用的是Java5或更高版本--那么在阅读之前可以使用volatile关键字(在5+中该关键字确保发生)。

如果它是一个对象,那么您需要使用某种类型的同步,或者使用synchronized关键字,或者使用java.util.concurrent包中的一个类,这些类的性能通常比简单的同步要好。例如,ConcurrentHashMap通常比Collections.synchronizedMap(Map)具有更好的性能,因为它对每个bin使用单独的锁,所以锁争用较少。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19340353

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档