zoukankan      html  css  js  c++  java
  • 并发编程大师系列之:Synchronized的类锁和对象锁

    说到并发编程,感觉跟大多数人一样,谈之色变,说它简单把,其实很有内容,说难吧,用起来也挺容易,最近我硬着头皮,决心要把并发编程好好的搞一遍。以前,面试的时候,面试官问,并发编程会吗?嗯,接触过,就加一个synchronized关键字就好了,面试官微笑着说,嗯好。特喵的现在感觉来说,这俩low逼。本来写了几行的软文,但感觉在技术文章里面体现,有失风度,明明可以靠文采吃饭,而我却非要靠技术,任性!上代码!

    1.对象锁概念:

    java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。

    代码格式:

        // 对象锁:形式1(方法锁)
        public synchronized void Method1() {
            System.out.println("我是对象锁也是方法锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        // 对象锁:形式2(代码块形式)
        public void Method2() {
            synchronized (this) {
                System.out.println("我是对象锁");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }

    2.类锁的概念:

    由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。

    代码格式:

        public synchronized static void Method3() {
            System.out.println("我是类锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }

    3.代码演示类锁和对象锁

    下面这段代码是两个类锁和一个对象锁,拿到锁后,睡1秒钟。

        // 类锁A
        public synchronized static void classLockA() {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    
        // 类锁B
        public synchronized static void classLockB() {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    
        // 对象锁
        public synchronized void objectLock() {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
    
        }

    创建三个线程类:分别调用一个资源中的三个方法

    class ThreadA extends Thread {
        private Test02 test02;
        public ThreadA(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
            test02.classLockA();
        }
    }
    
    class ThreadB extends Thread {
        private Test02 test02;
        public ThreadB(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
            test02.classLockB();
        }
    }
    
    class ThreadC extends Thread {
        private Test02 test02;
        public ThreadC(Test02 tk) {
            test02 = tk;
        }
        // 调用对象锁
        public void run() {
            test02.objectLock();
        }
    }

    main方法:起了三个线程,共同访问一个Test02对象

        public static void main(String[] args){
            
            Test02 test02 = new Test02();
            ThreadA ta = new ThreadA(test02);
            ThreadB tb = new ThreadB(test02);
            ThreadC tc = new ThreadC(test02);
    
            ta.setName("A");
            tb.setName("B");
            tc.setName("C");
    
            ta.start();
            tb.start();
            tc.start();
        }

    执行的结果:

    name = A, begain
    name = C, begain
    name = A, end
    name = B, begain
    name = C, end
    name = B, end

    可以看出由于 classLockA和classLockB都是类锁,即同一个锁,所以 A和B是按顺序执行,即同步的。而C是对象锁,和A/B不是同一种锁,所以C和A、B是 异步执行的。

    分析:

    对象锁要想保持同步执行,那么锁住的必须是同一个对象,举个例子:

    Test02类不变,重起两个线程类:均对对象锁进行了调用

    class ThreadA extends Thread {
        private Test02 test02;
        public ThreadA(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
            test02.objectLock();
        }
    }
    
    class ThreadB extends Thread {
        private Test02 test02;
        public ThreadB(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
            test02.objectLock();
        }
    }

    main方法:创建两个不同的资源对象,启动两个线程,分别对加锁的方法进行调用

        public static void main(String[] args){
    
            Test02 test02 = new Test02();
            Test02 test03 = new Test02();
            ThreadA ta = new ThreadA(test02);
            ThreadB tb = new ThreadB(test03);
    
            ta.setName("A");
            tb.setName("B");
    
            ta.start();
            tb.start();
        }

    结果如下:

    name = A, begain
    name = B, begain
    name = A, end
    name = B, end

    可见,是异步执行的,没有达到同步的作用。

    改进:只需对类锁进行调用,代码如下:

    class ThreadA extends Thread {
        private Test02 test02;
        public ThreadA(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
    //        test02.objectLock();
            test02.classLockA();
        }
    }
    
    class ThreadB extends Thread {
        private Test02 test02;
        public ThreadB(Test02 tk) {
            test02 = tk;
        }
        // 调用类锁
        public void run() {
    //        test02.objectLock();
            test02.classLockA();
        }
    }

    main方法:同样是创建了多个对象

        public static void main(String[] args){
    
            Test02 test02 = new Test02();
            Test02 test03 = new Test02();
            ThreadA ta = new ThreadA(test02);
            ThreadB tb = new ThreadB(test03);
    
            ta.setName("A");
            tb.setName("B");
    
            ta.start();
            tb.start();
        }

    结果:

    name = A, begain
    name = A, end
    name = B, begain
    name = B, end

    达到了同步的效果!

    总结:

    1. 如果多线程同时访问同一类的 类锁(synchronized 修饰的静态方法)以及对象锁(synchronized 修饰的非静态方法)这两个方法执行是异步的,原因:类锁和对象锁是2中不同的锁。 
    2. 类锁对该类的所有对象都能起作用,而对象锁不能。

  • 相关阅读:
    玩转web之javaScript(五)---js和jquery一些不可不知的方法(input篇)
    设计模式 外观模式 一键电影模式
    设计模式 适配器模式 以手机充电器为例
    高仿微信5.2.1主界面架构 包含消息通知
    Java进阶 创建和销毁对象
    sql语句中单引号嵌套问题
    Spark SQL UDF和UDAF示例
    Spark Parquet使用
    iptables只允许指定ip访问本机的指定端口
    Spark On YARN内存和CPU分配
  • 原文地址:https://www.cnblogs.com/zhangjianbing/p/9251712.html
Copyright © 2011-2022 走看看