Validity of the concurrency test for instance and static methods
I wanted to validate the below test I wrote to validate the fact that two threads can concurrently access a static synchronized method and a non-static synchronized method (as locks is on different objects). I got a result but wanted to know if my interpretation is correct or not
I ran the below code and I saw same value for variable i being printed at times from static and non-static method respectively. Is this a valid proof of the fact that static and non-static methods have locks on two different objects and two threads can access them simultaneously.
Code
import java.util.ArrayList;
import java.util.List;
public class TestStaticSynchronize {
public static final TesteeClass obj = new TesteeClass();
/**
* @param args
*/
public static void main(String[] args) {
for(int i = 0; i < 50; i++) {
开发者_Python百科 Runner run = new Runner(i);
Thread th = new Thread(run);
th.start();
}
}
static class Runner implements Runnable {
private int i;
public Runner(int i) {
this.i = i;
}
public void run() {
if(i % 2 == 0) {
TesteeClass.staticSync();
} else {
obj.instanceSync();
}
}
}
}
class TesteeClass {
private static List<Integer> testList = new ArrayList<Integer>();
public static synchronized void staticSync() {
System.out.println("Reached static synchronized method " + testList.size());
testList.add(1);
}
public synchronized void instanceSync() {
System.out.println("Reach instance synchronized method " + testList.size());
testList.add(1);
}
}
Your assessment is correct. Here's why.
So take your synchronized instance method and let's rewrite it in equivalent synchronized block notation:
public void instanceSync() {
synchronized( this ) {
System.out.println("...");
testList.add( 1 );
}
}
When you write a synchronized method it's the same thing as locking on the surrounding instance (i.e. this). With static methods this parameter does not exist so what is the equivalent synchronized block for statics? It's locking on the Class object.
public void classSync() {
synchronized( TestClass.class ) {
System.out.println("...");
testList.add( 1 );
}
}
So the instance this is different object from the object representing the TestClass class. That means there are two different locks being used which leads to the problems you discovered. In the end your test program is very dangerous and not thread safe. Instance methods, especially when used in multi-threaded situations, should NOT touch static members period. It's fine to route those accesses through static methods, but direct access is at best poor form at worse a serious bug.
There is a way to write your program in a way that they both lock on the same object, but I think it's important you consider why you are write code like this. Is it because you really only want lots of places to share a single structure like this, but have trouble getting a reference to a single object? This gets at the heart of software architecture and the important role it plays in multi-threaded applications. I suspect there is a much better option for you than using static members, and just use a single instance that all locations have a reference too (hopefully not using singleton pattern, global statics, etc).
精彩评论