2

我有一个线程用值填充向量对象,另一个线程定期从中获取值并定期清除它。

我希望访问向量的任一线程暂停,而另一个正在访问它。我需要使用 wait/notify/notifyAll 关键字吗?

private Vector<Long> _recordIdsSent;

# Called by main thread (before thread A and thread B are started)
public ConstructorOfThisClass() {
    _recordIdsSent = new Vector<Long>();
    // Starts thread A & B
}

# Called by thread A
public void addSentRecordIds( List<Long> ids ) {
    synchronized (_recordIdsSent) {
        _recordIdsSent.addAll( ids );
    }
}

# Called by thread B
public void deleteRecords()
{
    List<Long> ids;
    synchronized (_recordIdsSent) {
        ids = (List<Long>) _recordIdsSent.clone();
        _recordIdsSent.clear();
    }

    // Delete the records matching ids....
}

注意:我克隆了 _recordIdsSent 向量,因为删除操作可能需要一些时间。

[编辑] 将同步关键字从方法签名移动到变量 _recordIdsSent

4

5 回答 5

4

你不必。只需将一个同步块放入addSentRecordIds就像deleteRecords. 因此,您一次只能访问您的 Vector 一个线程。

于 2011-07-26T17:03:36.067 回答
1

如前所述,在提供的代码中对 vector 的访问addSentRecordIds是不同步的——这会使您的代码不安全

此外,此代码片段不能保证用于锁定 ( _recordIdsSent) 的对象不会更改 - 这也使其不安全。我自己通常更喜欢用于锁定的专用对象,因为它使我的意图更清晰并且不易出错。像这样:

private final Object lock = new Object(); // dedicated lock object
//... _recordIdsSent, addSentRecordIds etc...
public void deleteRecords()
{
    List<Long> ids;
    synchronized (lock) {
        ids = (List<Long>) _recordIdsSent.clone();
        _recordIdsSent.clear();
   }

   // Delete the records matching ids....
}
于 2011-07-26T17:24:11.417 回答
1

这不是对原始问题的回答,而是对解决手头问题的不同方式的建议。

您可以使用单个线程池(java.util.concurrent):

Executor executor = Executors.newSingleThreadExecutor();

当您想写入/删除数据库时:

executor.call(new Runnable() {
   @Override public void run() { ... write to DB}
});

executor.call(new Runnable() {
   @Override public void run() { ... delete in DB}
});

无论你在哪里调用它们,它们都会在同一个线程中运行。

编辑:来自 newSingleThreadExecutor 的 javadoc:“创建一个执行器,它使用单个工作线程在无界队列上运行。(但是请注意,如果该单个线程由于在关闭之前的执行过程中出现故障而终止,则一个新线程将取代它的位置如果需要执行后续任务。)任务保证按顺序执行,并且在任何给定时间不会有超过一个任务处于活动状态。与其他等效的 newFixedThreadPool(1) 不同,返回的执行程序保证不可重新配置以使用额外的线程。”

于 2011-07-27T16:36:27.813 回答
0

您不必这样做,因为同步关键字正在为您执行此操作。

同步意味着一次只能有一个线程在该块或类中的任何其他同步块中。这意味着如果该类中有两个不同的对象需要线程安全,并且您使操作这两个不同对象的方法同步,那么访问其中一个对象的线程将阻止其他线程访问另一个对象对象(不理想),所以不要过度使用它,否则你会抵消拥有多线程的所有好处。

于 2011-07-26T17:14:58.430 回答
0

如前文所述,在修改数据结构的方法上使用 synchronized 关键字将防止并发访问和潜在的数据损坏。

使用等待和通知方法的价值在于消费者线程(读取数据的线程)不必不断轮询,而是可以在向量达到一定大小时由生产者线程通知。

于 2011-07-26T17:54:06.403 回答