- 设想**
多个线程向ArrayList
* list * 添加对象,该列表是ListHolder
对象 * listHolder * 的属性,所有线程共享该属性,线程调用ListHolder.addObject()
向 * list * 添加对象,addObject()方法包含一个 * synchronized
**块 *,它将 * list * 作为 * 监视对象 * 传递,以防止多线程错误。(以独立且异步的方式)每个线程将调用方法ListHolder.processList()
,该方法对 * list * 执行操作并使其为空。
如果processList()
是一个 * synchronized * 方法,当它正在使用时,试图调用它的线程将等待,直到阻塞线程完成,然后再调用它。
- 问题**
但是,如何使调用processList()
的线程在被另一个线程使用(阻塞)时不等待,而是移动到下一行代码?
这将大大提高效率,因为一旦被阻塞的线程完成,它将尝试处理一个空的 * list *,而它可以构造要添加到 * list * 的对象,而不是等待。
1条答案
按热度按时间wlwcrazw1#
我能想到至少两个可能的解决办法。
1.使用
Semaphore
。1.处理列表的副本而不持有锁。
信号量
你可以创建一个
Semaphore
,它有一个名为tryAcquire()
的方法,试图立即获得一个许可;如果有一个可用,则返回true
,否则返回false
。例如:
这将只允许一个线程在任何给定的时间处理列表,如果一个线程已经在处理列表,那么另一个线程将直接从
processList()
返回,而不做任何事情。这种方法的缺点是,当一个线程处理列表时,没有其他线程可以添加到列表中,这可能会给生产者带来瓶颈。
处理副本
如果您的方案允许,您可以改为复制列表,清除列表,然后在不持有锁的情况下处理副本。您将仅在创建副本时同步列表。
例如:
复制列表应该只需要相对于处理很短的时间,假设处理是昂贵的,这样线程就不会持有锁太长时间。
这种方法的优点是多个线程可以同时处理 * 不同的数据集 *,同时仍然允许其他线程相对不受阻碍地构造和添加对象到列表中。但是,如果由于您的问题中没有说明的原因,您需要在整个处理操作期间持有锁,或者如果两个线程不能同时处理元素,则这种方法是不可行的。
额外好处:混合解决方案
你也可以考虑以上两种解决方案的混合,使用
Semaphore
,这样在任何给定的时间只有一个线程可以处理列表,但是处理一个副本,这样其他线程可以继续向列表添加对象。例如: