Reputation: 22402
I have got the below sample code (uses Java), which will be accessed from multiple threads.
public class ApplicationProducts {
private final List<Product> myAppProducts = Collections.synchronizedList(new ArrayList<>());
private String firstPositionProductName;
private int firstPositionProductAvailability;
public void updateProducts(List<Product> products) {
myAppProducts.addAll(products);
}
public boolean checkAndReplaceFirstProduct(Product product) {
synchronized(myAppProducts) {
if((product.getId()).equals(myAppProducts.get(0).getId())) {
//calculations goes here to derive the below details
firstPositionProductAVailability = 10;
firstPositionProductName = abc;
return true;
} else {
return false;
}
}
}
public boolean removeFirstProduct(Product product) {
synchronized(myAppProducts) {
if((product.getId()).equals(myAppProducts.get(0).getId()) &(firstPositionProductName.getId().equals(myAppProducts.get(0).getId())) {
myAppProducts.remove(0);
return true;
} else {
throw new IllegalStateException(" Lock taken by some other product : product can't be removed ");
}
}
}
}
I have got the below queries, could you please help ?
(1) Is the above class thread safe ?
(2) Do I really need 'synchronization' on 'myAppProducts' as I am already checking for some condition i.e., will this class be thread safe after removing 'synchronized' from both methods ? One important point is that in my application, each thread calls checkAndReplaceFirstProduct() first and if the result is true (means acquired the product and does some calculations) and then only calls removeFirstProduct() ?
(3) Do I need to add 'synchronization' inside updateProducts() as well, which simply appends the more products to the existing list ?
(4) I know that synchronizedList's Iterator should be part of 'synchronized' block (https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList(java.util.List), does it require the lock (synchronization) for accessing the 'synchronizedList.get(index)' method (like in above) ?
Upvotes: 2
Views: 677
Reputation: 31053
Yes, you need the synchronized
blocks, but not for any reason related to myAppProducts
itself: observe your usage of firstPositionProductAVailability
and firstPositionProductAvailability
. As it is now, access to those members is protected by the synchronized
blocks. If you remove those blocks, you'd introduce a potential race condition with respect to those members!
Accessing myAppProducts
alone does not require the synchronized
blocks. All method calls on the wrapped collection are properly interlocked by the wrapper, with the notable exception of iteration (as is called out explicitly in the documentation).
In general, you will need the synchronized
blocks, if
for
/iterator
Regarding your edited
(2) Do I really need 'synchronization' on 'myAppProducts' as I am already checking for some condition i.e., will this class be thread safe after removing 'synchronized' from both methods ? One important point is that in my application, each thread calls checkAndReplaceFirstProduct() first and if the result is true (means acquired the product and does some calculations) and then only calls removeFirstProduct() ?
Here, you have a real race condition, which you cannot easily solve without changing the interface and/or with explicit cooperation by the callers of those methods. A possible solution would be to expose the object, on which we need to synchronize, so that the client of this class can to the right thing:
public Object getSynchronizationObject() {
return myAppProducts;
}
and document, that your clients have to do
synchronized (obj.getSynchronizationObject()) {
if (obj.checkAndReplaceFirstProduct(something)) {
obj.removeFirstProduct(something);
}
}
I mention this (but I do not recommend it!) since this is the approach taken by Java's Collections.synchronizedXxx
with respect to iteration. A better solution would be to rework the interface, but I don't know anything about your actual use case, so I find it hard to come up with a proposal here.
Upvotes: 2
Reputation: 328568
Is the above class thread safe ?
Yes it looks fine. But I would add some checks before accessing myAppProducts.get(0)
and myAppProducts.remove(0)
(what if the list is empty?).
Do I really need 'synchronization' on 'myAppProducts' as I am already checking for some condition i.e., will this class be thread safe after removing 'synchronized' from both methods ?
Yes you do based on your code design:
if
condition may be true when you test it but become false by the time you reach the following lineDo I need to add 'synchronization' inside updateProducts() as well, which simply appends the more products to the existing list ?
No addAll
is synchronized.
I know that synchronizedList's Iterator should be part of 'synchronized' block. Do I need to use a lock when calling 'synchronizedList.get(index)'?
No you don't, get
is already synchronized.
Upvotes: 2