多类加载器环境下使用synchronized的注意事项

本文探讨了使用synchronized关键字对obj.getClass()加锁时,如何影响不同类加载器加载的类实例之间的并发行为。通过示例代码展示了当使用自定义类加载器时,由于每个类加载器加载的类具有独立的Class对象,导致synchronized无法实现预期的同步效果。分析了这种现象的原因,并强调了类加载器在并发控制中的作用。
摘要由CSDN通过智能技术生成

  如果使用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的,所以这种情况两个线程加的是不同的锁,也就自然无法达到互斥效果~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值