Synchronized 深度解析

synchronized可修饰普通方法、静态方法和代码块 。

修饰普通方法,锁的是对象,一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。

public synchronized void sayHello(){
}

修饰静态方法,锁的是类(new多个对象都是源于同一个类,同步静态方法仍然互锁)

此类所有的实例对象,都无法同时访问类中的所有同步静态(synchronized static)方法

public synchronized static void sayHello(){
}

修饰代码块,取决于锁的是什么(synchronized(this)    synchronized(My.class) )

synchronized(this){
}

 

1 原理

synchronized是基于JVM层面的,lock是基于JDK层面的

  • 代码块同步原理
package com.paddx.test.concurrent;

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("Method 1 start");
        }
    }
}

javap  进行class文件反编译

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

3、如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程都可以尝试去获取这个 monitor 的所有权,所以是非公平锁。 

  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

  • 同步方法原理
package com.paddx.test.concurrent;

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}

2 几种锁类型

synchronized按锁的量级从轻到重分为:无锁--->  偏向锁--->轻量锁---->重量锁       锁只能升级不能降级

偏向锁:如果目前只有一个线程多次获得锁,就没必要下次再用锁的时候重新竞争,轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令,下次还是这个线程来用对象就可以无需验证直接获得锁。如果有多个线程抢用锁,则撤销偏向锁并膨胀为轻量锁。   无阻塞   无自旋    加锁解锁无消耗  适用于无多线程执行同步代码的情况

java ååé - silver9886@126 - silver9886@126çå客

轻量锁:竞争锁的线程首先需要拷贝对象头中的Mark Word到帧栈的锁记录中。拷贝成功后使用CAS操作尝试将对象的Mark Word更新为指向当前线程的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁。如果更新失败,那么意味着有多个线程在竞争。
当竞争线程尝试占用轻量级锁失败多次之后(使用自旋)轻量级锁就会膨胀为重量级锁,重量级线程指针指向竞争线程,竞争线程也会阻塞,等待轻量级线程释放锁后唤醒他。     有自旋   无阻塞    占用CPU   适用同步代码执行快的情况

重量锁:竞争失败后,线程阻塞,释放锁后,唤醒阻塞的线程,不使用自旋锁,不会那么消耗CPU,所以重量级锁适合用在同步块执行时间长的情况下。     有阻塞      无自旋

 

还有其他几种锁类型作为延伸阅读。

互斥锁mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒 

自旋锁spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取锁,虽然CPU的时间被消耗了,但是比线程下文切换时间要少。这个时候使用自旋是划算的。

读写锁:rwlock,区分读和写,处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 
注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写优先于读,当有线程因为等待写锁而进入睡眠时,则后续读者也必须等待 
适用于读取数据的频率远远大于写数据的频率的场合。 

3 synchronized抢占锁实例

3.1 synchronized(this)   

public class Synchronized {

    public  void method1(){
        synchronized (this) {
            System.out.println("method1 start");
            try {
                System.out.println("Method 1 execute");
                Thread.sleep(3000);
            } catch (Exception e) {

            }
            System.out.println("method1 end");
        }
    }

    public  void method2(){
        synchronized (this) {
            System.out.println("method2 start");
            try {
                System.out.println("Method 2 execute");
                Thread.sleep(1000);
            } catch (Exception e) {

            }
            System.out.println("method2 end");
        }
    }

    public static void main(String[] args) {
        System.out.println("程序运行");
        Synchronized syn = new Synchronized();
        // Synchronized syn2 = new Synchronized();
        new Thread(()->{
            syn.method1();
        }).start();
        new Thread(()->{
            syn.method2();
        }).start();

    }
}

结果自然是先执行method1,再执行method2

程序运行
method1 start
Method 1 execute
method1 end
method2 start
Method 2 execute
method2 end

3.2 synchronized(*.class)

public class Synchronized {

    public  void method1(){
        synchronized (Synchronized.class) {
            System.out.println("method1 start");
            try {
                System.out.println("Method 1 execute");
                Thread.sleep(3000);
            } catch (Exception e) {

            }
            System.out.println("method1 end");
        }
    }

    public  void method2(){
        synchronized (Synchronized.class) {
            System.out.println("method2 start");
            try {
                System.out.println("Method 2 execute");
                Thread.sleep(1000);
            } catch (Exception e) {

            }
            System.out.println("method2 end");
        }
    }

    public static void main(String[] args) {
        System.out.println("程序运行");
        Synchronized syn = new Synchronized();
        Synchronized syn2 = new Synchronized();
        new Thread(()->{
            syn.method1();
        }).start();
        new Thread(()->{
            syn2.method2();
        }).start();

    }
}

锁的是类,那么该类生成的所有实例对象都会被锁住。

程序运行
method1 start
Method 1 execute
method1 end
method2 start
Method 2 execute
method2 end

3.3 synchronized(*.class)      synchronized(this)

public class Synchronized {

    public  void method1(){
        synchronized (Synchronized.class) {
            System.out.println("method1 start");
            try {
                System.out.println("Method 1 execute");
                Thread.sleep(3000);
            } catch (Exception e) {

            }
            System.out.println("method1 end");
        }
    }

    public  void method2(){
        synchronized (this) {
            System.out.println("method2 start");
            try {
                System.out.println("Method 2 execute");
                Thread.sleep(1000);
            } catch (Exception e) {

            }
            System.out.println("method2 end");
        }
    }

    public static void main(String[] args) {
        System.out.println("程序运行");
        Synchronized syn = new Synchronized();
        // Synchronized syn2 = new Synchronized();
        new Thread(()->{
            syn.method1();
        }).start();
        new Thread(()->{
            syn.method2();
        }).start();

    }
}

一个锁的是 .class文件,一个锁的是对象,那么到底这两个方法会互斥执行么?

答案是不会,因为本质上锁的就不是同一个对象!!!

程序运行
method1 start
Method 1 execute
method2 start
Method 2 execute
method2 end
method1 end

3.4 synchronized  static 方法

public class Synchronized {

    public  synchronized static void method1(){

        System.out.println("method1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (Exception e) {

        }
        System.out.println("method1 end");

    }

    public  synchronized static void method2(){
            System.out.println("method2 start");
            try {
                System.out.println("Method 2 execute");
                Thread.sleep(1000);
            } catch (Exception e) {

            }
            System.out.println("method2 end");
    }

    public static void main(String[] args) {
        System.out.println("程序运行");
        Synchronized syn = new Synchronized();
        Synchronized syn2 = new Synchronized();
        new Thread(()->{
            syn.method1();
        }).start();
        new Thread(()->{
            syn2.method2();
        }).start();

    }
}

锁加在静态方法上,锁的是class对象,那么new 的新对象自然也会抢占锁

3.5 synchronized static  和 synchronized 混合使用

public class Synchronized {

    public  synchronized static void method1(){

        System.out.println("method1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (Exception e) {

        }
        System.out.println("method1 end");

    }

    public  synchronized   void method2(){
            System.out.println("method2 start");
            try {
                System.out.println("Method 2 execute");
                Thread.sleep(1000);
            } catch (Exception e) {

            }
            System.out.println("method2 end");
    }

    public static void main(String[] args) {
        System.out.println("程序运行");
        Synchronized syn = new Synchronized();
        Synchronized syn2 = new Synchronized();
        new Thread(()->{
            syn.method1();
        }).start();
        new Thread(()->{
            syn2.method2();
        }).start();

    }
}

一个锁的class,一个锁的方法,自然不会发生互斥地现象。synchronized加在static方法上,只会锁其他synchronized static方法

 

基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip 个人大四的毕业设计、课程设计、作业、经导师指导并认可通过的高分设计项目,评审平均分达96.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 [资源说明] 不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的毕设或者课设、作业,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96.5分,放心下载使用! 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),供学习参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值