如果使用synchronized对obj.getClass()加锁,锁对象就是Class,那么对于这个Class下的所有实例对象访问都能达到同步控制的目的。比如以下代码:
public class JavaMain {
public void run() {
synchronized (this.getClass()) {
int i = 0;
while (i++ < 10) {
try {
System.out.println(Thread.currentThread().getId());
Thread.sleep(1000L);
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) {
JavaMain javaMain = new JavaMain();
JavaMain javaMain1 = new JavaMain();
new Thread(new Runnable() {
@Override
public void run() {
javaMain.run();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
javaMain1.run();
}
}).start();
}
}
在run方法中循环打印了当前的线程id,由于加锁使用的操作是:
synchronized(this.getClass())
所以不会有并发问题,输出结果就是一个线程先,另一个线程后:
11
11
11
11
11
11
11
11
11
11
12
12
12
12
12
12
12
接下来,我们使用自定义类加载来加载这个类再试试。首先弄一个简单的类加载器:
public class TestClassLoader extends URLClassLoader {
public TestClassLoader(URL[] urls) {
super(urls);
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
if ("com.demo.classloadersyn.JavaMain".equals(name)) {
c = findClass(name);
} else {
return super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
然后分别使用两个类加载器来加载JavaMain这个类,创建两个实例对象,run方法还是不变,使用this.getClass()进行加锁:
public class JavaMain {
public void run() {
synchronized (this.getClass()) {
int i = 0;
while (i++ < 10) {
try {
System.out.println(Thread.currentThread().getId());
Thread.sleep(1000L);
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) throws Exception {
String classPath = JavaMain.class.getClassLoader().getResource("").getPath();
URL[] urls = new URL[]{new File(classPath).toURI().toURL()};
TestClassLoader classLoader1 = new TestClassLoader(urls);
TestClassLoader classLoader2 = new TestClassLoader(urls);
Class clazz1 = classLoader1.loadClass("com.demo.classloadersyn.JavaMain");
Class clazz2 = classLoader2.loadClass("com.demo.classloadersyn.JavaMain");
Object obj1 = clazz1.newInstance();
Object obj2 = clazz2.newInstance();
Method method1 = obj1.getClass().getDeclaredMethod("run");
Method method2 = obj2.getClass().getDeclaredMethod("run");
new Thread(new Runnable() {
@Override
public void run() {
try {
method1.invoke(obj1);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
method2.invoke(obj2);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}).start();
}
}
这会儿再来看输出,就发现并发控制并没有生效:
11
12
11
12
11
12
12
11
12
11
12
11
12
11
12
11
12
11
12
11
原因很简单,通过不同的类加载器加载的类实例化的对象,调用其getClass()方法返回的是各自的Class,而虚拟机中是通过类加载器和全限定名唯一确定一个Class的,所以这种情况两个线程加的是不同的锁,也就自然无法达到互斥效果~