在不使用wait关键字的情况下同步块是否安全?



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

我希望访问向量的任何一个线程在另一个线程访问它时暂停。是否需要使用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向量,因为删除操作可能需要一些时间。

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

你不需要。在addSentRecordIds中放一个同步块就像在deleteRecords中一样。所以,你一次只能访问一个Vector线程。

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

同样,这个代码片段不能保证用于锁定(_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....
}

这不是对最初问题的回答,而是对解决当前问题的另一种方法的建议。

您可以使用单个线程池(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}
});

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

编辑:从javadoc的newSingleThreadExecutor:"创建使用单个工作线程在无界队列上操作的执行器。(但是请注意,如果这个单线程在关闭之前的执行过程中由于失败而终止,如果需要执行后续任务,将会有一个新的线程取代它。)任务保证按顺序执行,并且在任何给定时间活动的任务不超过一个。与newFixedThreadPool(1)不同,返回的执行器保证不会被重新配置以使用其他线程。"

在前面的文章中提到,在修改数据结构的方法上使用synchronized关键字可以防止并发访问和潜在的数据损坏。

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

您不必这样做,因为synchronized关键字正在为您做这些。

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

最新更新