David
David

Reputation: 2008

Is it safe to synchronize a block without using the wait keyword?

I have one thread populating a vector object with values, and one other thread that periodically fetches the values from it and clears it periodically.

I want that either thread accessing the vector pauses while the other one is accessing it. Do I need to use the wait/notify/notifyAll keywords ?

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....
}

Note: I clone the _recordIdsSent vector because the delete operation can take some time.

[EDIT] Moved the synchronized keyword from the method signature to the variable _recordIdsSent

Upvotes: 2

Views: 311

Answers (5)

Oliver
Oliver

Reputation: 11597

You don't have to as the synchronized keyword is doing that for you.

Synchronized means that only one thread can be in that block, or any other synchronized block in the class, at a time. This means that if the class has two distinct objects in it that need to be thread safe, and you make the methods that manipulate both of those distinct objects as synchronized, then a thread accessing one of the objects will block other threads from accessing the other object (not ideal), so don't overuse it or you'll counteract all the benefits of having multithreading.

Upvotes: 0

toto2
toto2

Reputation: 5326

This is not an answer to the original question, but a suggestion on a different way of tackling the problem at hand.

You could use a single thread pool (java.util.concurrent):

Executor executor = Executors.newSingleThreadExecutor();

and when you want to write/delete to the DB:

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

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

Wherever you call those, they'll run in the same thread.

EDIT: from the javadoc of newSingleThreadExecutor: "Creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads."

Upvotes: 1

gnat
gnat

Reputation: 6230

As already noted, in provided code access to vector from addSentRecordIds is not synchronized - that makes your code unsafe

Also, this code snippet does not guarantee that object used for locking (_recordIdsSent) doesn't change - this makes it unsafe too. Myself I typically prefer dedicated objects for locking because it makes my intent cleaner and is less error prone. Like this:

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....
}

Upvotes: 1

Kailash
Kailash

Reputation: 785

As noted in the earlier posts, using the synchronized keyword on the methods modifying your data structure will prevent concurrent access and potential corruption of your data.

The value in using wait and notify methods is that the consumer thread (the one reading the data) doesn't have to constantly poll, rather it can be notified by the producer thread(s) when the vector has reached a certain size.

Upvotes: 0

You dont have to. Just put a synchronized block in addSentRecordIds just like deleteRecords. So, you will only access your Vector one thread at a time.

Upvotes: 4

Related Questions