zoukankan      html  css  js  c++  java
  • java synchronized 探究

    java synchronized关键字是并发编程最常用的工具,也是最重要的工具之一。今天来探究下其含义

    写两个类,两段不同的临界区代码进行测试。

    有以下4种测试方案,每种方案对应4种不同的锁类型:对象锁: synchronized(this),本类锁: synchronized(Me.class),父类锁: synchronized(Parent.class),自定义锁对象

      1. 同一个对象,在不同线程中运行

      2. 同一个类的不同对象,在不同线程中运行

      3. 不同类(有共同的父类)的不同对象,在不同线程中运行

      4. 不同类(没有共同的父类)的不同对象,在不同线程中运行

    测试结果:

      用对象锁,只有1可以互斥访问临界区;

      用本类锁,则1和2可以互斥访问临界区;

      用共同父类的锁,全部都可以互斥访问临界区。(注意,使用第4种测试方案时,两个对象已经没有共同的父类了,因此这个所谓共同父类的锁,其实是一个第3者,与这两个类没有直接关系,类似于自定义的锁对象,只不过这个自定的锁对象不是普通的对象,而是一个Class对象)

      用自定义的锁对象,全部都可以互斥访问临界区。

    /**
     * synchronized关键字测试
     *
     * @author zhangxz
     * @date 2019-11-18 11:07
     */
    
    public class SynchronizedTest {
    
        private static final Sync sync = new Sync();
    
        public static void main(String[] args) throws InterruptedException {
            ThreadA threadA1 = new ThreadA();
            ThreadA threadA2 = new ThreadA();
            ThreadB threadB = new ThreadB();
    
    //        testOneRunnable(threadA1);
    //        testTwoRunnable(threadA1, threadA2);
            testTwoRunnable(threadA1, threadB);
        }
    
        static class Sync{}
    
        //使用一个runnable对象,在两个不同线程调用
        static void testOneRunnable(Runnable runnable) {
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
    
            thread1.start();
            thread2.start();
        }
    
        //使用两个runnable对象,在两个不同线程调用
        static void testTwoRunnable(Runnable runnable1, Runnable runnable2) {
            Thread thread1 = new Thread(runnable1);
            Thread thread2 = new Thread(runnable2);
    
            thread1.start();
            thread2.start();
        }
    
        static class ABParent {
        }
    
        static class ThreadA/* extends ABParent */implements Runnable {
            @Override
            public void run() {
    //            synchronized (this) {
    //            synchronized (ThreadA.class) {
    //            synchronized (ABParent.class) {
                synchronized (sync) {
                    try {
                        Thread.sleep(10);
                        System.out.println("process in thread a " + Thread.currentThread().getName());
                        Thread.sleep(10);
                        System.out.println("process in thread a " + Thread.currentThread().getName());
                        Thread.sleep(10);
                        System.out.println("process in thread a " + Thread.currentThread().getName());
                        Thread.sleep(10);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
    
                }
            }
        }
    
        static class ThreadB /*extends ABParent*/ implements Runnable {
            @Override
            public void run() {
    //            synchronized (this) {
    //            synchronized (ThreadB.class) {
    //            synchronized (ABParent.class) {
                synchronized (sync) {
                    try {
                        Thread.sleep(10);
                        System.out.println("process in thread b " + Thread.currentThread().getName());
                        Thread.sleep(10);
                        System.out.println("process in thread b " + Thread.currentThread().getName());
                        Thread.sleep(10);
                        System.out.println("process in thread b " + Thread.currentThread().getName());
                        Thread.sleep(10);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }
    
    
    }

    另外的测试  

      1. 把其中一个类的synchronized去除,即一个是临界区,另外一个不是。然后对其测试,发现无论临界区用的哪种锁,两者都无法实现互斥访问。

      2. 把两个类的锁对象改为不同的锁,则两个synchronized代码块因为持有不同的锁,无法构成临界区,因此也无法实现互斥访问。

      3. 测试同一个对象的不同synchronized方法,如下代码。测试结果:同一个对象的不同synchronized方法构成同一个临界区,可以互斥访问。

      

        static void testDifferentMethodsInOneObject(TestA testA) {
            Runnable runnable1 = () -> {
                try {
                    testA.MethodA();
                    testA.MethodB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
    
            Runnable runnable2 = () -> {
                try {
                    testA.MethodB();
                    testA.MethodA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
    
            Thread thread1 = new Thread(runnable1);
            Thread thread2 = new Thread(runnable2);
    
            thread1.start();
            thread2.start();
    
        }
    
        static class TestA {
            public synchronized void MethodA() throws InterruptedException {
                Thread.sleep(10);
                System.out.println("process in method a " + Thread.currentThread().getName());
                Thread.sleep(10);
                System.out.println("process in method a " + Thread.currentThread().getName());
                Thread.sleep(10);
                System.out.println("process in method a " + Thread.currentThread().getName());
                Thread.sleep(10);
            }
    
            public synchronized void MethodB() throws InterruptedException {
                Thread.sleep(10);
                System.out.println("process in method b " + Thread.currentThread().getName());
                Thread.sleep(10);
                System.out.println("process in method b " + Thread.currentThread().getName());
                Thread.sleep(10);
                System.out.println("process in method b " + Thread.currentThread().getName());
                Thread.sleep(10);
            }
        }

    synchronized 使用的最终结论:

      1. 临界区和非临界区是无法实现互斥访问的。

      2. 同一个对象的临界区(即使是不同的synchronized代码块,也只能构成同一个临界区,因为synchronized锁的最小粒度是对象锁),只要加锁就可以互斥访问。

      3. 同一个类的不同对象/不同类的不同对象,两者的synchronized代码块虽然不再同一个地方,但是只要锁的粒度足够大,大到覆盖了两个代码块,则两个代码块就构成了一个临界区,就能实现互斥访问。

      4. 使用自定义的公共变量作为锁对象,只要规定进入代码块必须获得同一把锁,则可以对于任何地方的临界区实现互斥访问。

    附加测试:

      当同一个对象,有两个方法,一个为static,一个普通的,然后两个方法都使用了synchronized,那么这两个方法可以实现互斥访问吗?

      答案是否定的,因为两个方法持有的锁是不同的两把锁,一个为类锁,一个为对象锁,因此两者不能构成临界区。(我一开始的思维是,两把锁,一个锁比较大,另外一个锁比较小,那么大的锁是覆盖了小粒度的锁的,但实验证明这个想当然是错误的。)代码如下:

    /**
     * synchronized测试类2
     *
     * @author zhangxz
     * @date 2019-11-18 21:43
     */
    
    public class SynchronizedTest2 {
    
        public static void main(String[] args) {
            TestA testA = new TestA();
            new Thread(() -> {
                testA.a();
            }).start();
    
            new Thread(() -> {
                testA.b();
            }).start();
        }
    
        static class TestA {
            static synchronized void a() {
    
                System.out.println("i am method a.");
                Thread.yield();
                System.out.println("i am method a.");
                Thread.yield();
                System.out.println("i am method a.");
            }
    
            synchronized void b() {
    
                System.out.println("i am method b.");
                Thread.yield();
                System.out.println("i am method b.");
                Thread.yield();
                System.out.println("i am method b.");
            }
        }
    
    }

    上面测试用到了 Thread类的 yield方法,这个方法的效果是当前线程做出cpu时间片的让步,在当前线程执行后续步骤之前,让其他线程有机会得到cpu的执行时间。在测试多线程问题中比较常用。

    参考链接: 

      https://www.cnblogs.com/java-spring/p/8309931.html

  • 相关阅读:
    编译报错:实际参数列表和形式参数列表长度不同
    狂神说 GUI编程笔记
    狂神说 JavaSE入门笔记(三)
    狂神说 JavaSE入门笔记(二)
    狂神说 JavaSE入门笔记(一)
    安装ros的问题
    /opt/ros/kinetic/include/moveit/macros/declare_ptr.h:53:16: error: ‘shared_ptr’ in namespace ‘std’ does not name a template type typedef std::shared_ptr<const Type> Name##ConstPtr;
    [ERROR] [1583298467.643559437]: Exception while loading planner 'ompl_interface/OMPLPlanner': According to the loaded
    Linux常用命令大全
    机器人感知
  • 原文地址:https://www.cnblogs.com/zhangxuezhi/p/11881417.html
Copyright © 2011-2022 走看看