The following example shows, although vector is thread safe, getLast and deleteLast didn't use client-side locking to guard compound actions. As a result, getLast sometimes can through ArrayIndexOutOfBoundsException.
import java.util.Vector;
public class UnsafeVectorHelpersTest{
public static void main(String...args) {
Vector<String> list = new Vector<String>(10);
for(int i = 0; i < 10; i++) {
list.addElement("a");
}
System.out.println(list.size());
new Thread(new Runnable(){
@Override
public void run() {
UnsafeVectorHelpers.deleteLast(list);
}}).start();
UnsafeVectorHelpers.getLast(list);
}
}
class UnsafeVectorHelpers {
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
The synchronized collection classes guard each method with the lock on the synchronized collection object itself, synchronized collections also supports client-side locking. By holding the vector as the
client-side lock, we can make the getLast and deleteLast atomic, thus fixed the check-then-act race condition we observed in UnsafeVectorHelper. We should not use this as the client-side lock, because this lock refers to the intrinsic lock of SafeVectorHelper, which is the wrong lock -- the vector guard each method with vector's intrinsic lock instead of the the caller's intrinsic lock (SafeVectorHelper)
import java.util.Vector;
public class SafeVectorHelpersTest{
public static void main(String...args) {
Vector<String> list = new Vector<String>(10);
for(int i = 0; i < 10; i++) {
list.addElement("a");
}
System.out.println(list.size());
new Thread(new Runnable(){
@Override
public void run() {
SafeVectorHelpers.deleteLast(list);
}}).start();
SafeVectorHelpers.getLast(list);
}
}
class SafeVectorHelpers {
public static Object getLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
}
public static void deleteLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
}