关于synchronized在多线程中的几种锁类型和底层原理解析

 

  • 1. 对象锁 类锁
    非static方法加synchronized锁,static方法加synchronized锁,不加synchronized锁 三种调用顺序比较
    
    public class TheadStaticLock {
    
        public synchronized  void m1(){
            System.out
                    .println(Thread.currentThread().getName()+" m1 start");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" m1 end");
        }
    
        public synchronized static void m2(){
            System.out.println(Thread.currentThread().getName()+" m2 start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" m2 end");
        }
        public   void m3(){
            System.out.println(Thread.currentThread().getName()+" m3 start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" m3 end");
        }
    
        public static void main(String[] args) {
            TheadStaticLock tl =new TheadStaticLock();
            new Thread(()->tl.m1()).start();
            new Thread(()->m2()).start();
            new Thread(()->tl.m3()).start();
    
        }
    }

看执行结果:

Thread-0 m1 start
Thread-1 m2 start
Thread-2 m3 start
Thread-1 m2 end
Thread-2 m3 end
Thread-0 m1 end

结论:说明m3 方法的执行并没有因为m1 m3方法的synchronzied对象锁而等待,而且m2方法没有因为m1方法的加锁而等待

m1方法synchronzied锁的是this对象,synchronzied他是一把对象锁,m2方法锁的是TheadStaticLock.class,我们知道static方法是没有this对象的,此时的synchronzied他是一把类锁,而m3方法压根就没有上任何锁。

2 可重入锁

同类型的锁类型在同类下是可重入的,他其实是一把锁,如果不是一把锁的话,互相等待那样就会发生死锁,Java肯定不能容忍这样的事情发生,因此这把锁是可重入锁定,锁上再加锁,上一把锁释放然后再给另一个同样持有这把锁的方法执行,看代码

public class ThreadReinLock {

    private int count=10;

    public synchronized void m1() throws InterruptedException {
        System.out.println("m1实验可重入锁...start");
        Thread.sleep(6000);
        System.out.println("m1实验可重入锁...end");
        m2();
    }

    public synchronized void m2() throws InterruptedException {
        System.out.println("m2对象锁与m1是一把锁...start");
        Thread.sleep(4000);
        System.out.println("m2对象锁与m1是一把锁...end");
    }
    public synchronized  static void m3() throws InterruptedException {
        System.out.println("m3类锁与m1不是一把锁...,可以同时调用start");
        Thread.sleep(1000);
        System.out.println("m3类锁与m1不是一把锁...,可以同时调用end");
    }
    public String m4() throws InterruptedException {
        System.out.println("m4类锁与m1不是一把锁...,可以同时调用start");
        Thread.sleep(1000);
        System.out.println("m4类锁与m1不是一把锁...,可以同时调用end");
        return "";
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadReinLock trl=new ThreadReinLock();

        new Thread(()-> {
            try {
                trl.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()-> {
            try {
                trl.m2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        m3();//静态方法是没有this对象的
        new Thread(()-> {
            try {
                trl.m4();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

看执行结果:

m1实验可重入锁...start
m3类锁与m1不是一把锁...,可以同时调用start
m3类锁与m1不是一把锁...,可以同时调用end
m4类锁与m1不是一把锁...,可以同时调用start
m4类锁与m1不是一把锁...,可以同时调用end
m1实验可重入锁...end
m2对象锁与m1是一把锁...start
m2对象锁与m1是一把锁...end
m2对象锁与m1是一把锁...start
m2对象锁与m1是一把锁...end

结论:m1 和m2 是一把锁,且是重入锁,m1执行完才可以执行m2,static 不会影响m3执行不是一把锁 非static 非synchronized方法也没有上锁也可以同时执行
  • 3 异常锁

异常锁,当锁方法在执行过程中发生异常,会释放掉锁 * thread0 在发生异常立刻释放对象锁 m2立刻拿到对象锁,立即执行

public class ThreadExceptionLock {

    private int count=100;

    synchronized  void m1(){
        System.out.println(Thread.currentThread().getName()+" m1 start");
        for(int i=0;i<100;i++){
            count--;
            System.out.println(Thread.currentThread().getName()+" m1 count="+count);
            if(count == 5){
                System.out.println(1/0);
            }
        }
        System.out.println(Thread.currentThread().getName()+" m1 end");
    }
    synchronized  void m2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+" m2 start");
        System.out.println("m2 拿到对象锁");
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+" m2 end");
    }

    public static void main(String[] args) {
        ThreadExceptionLock tel=new ThreadExceptionLock();
        new Thread(()->tel.m1()).start();
        new Thread(()-> {
            try {
                tel.m2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

看执行结果:

Thread-0 m1 count=8

  • Thread-0 m1 count=7

Thread-0 m1 count=6
Thread-0 m1 count=5
Thread-1 m2 start
m2 拿到对象锁Exception in thread "Thread-0" 
java.lang.ArithmeticException: / by zero
    at com.panmeng.learn.thread.ThreadExceptionLock.m1(ThreadExceptionLock.java:25)
    at com.panmeng.learn.thread.ThreadExceptionLock.lambda$main$0(ThreadExceptionLock.java:39)
    at java.lang.Thread.run(Thread.java:748)
Thread-1 m2 end
结论:当拿到synchronized的m1方法在执行过程中由于各种原因发生了异常,是立刻马上释放synchronized锁,从而m2方法立刻获取了synchronized锁,如果不想释放锁,可以在代码外围加上try catch代码块捕获异常,这样就不会释放锁

  • 偏向锁 自旋锁 重量锁

synchronized 方法在刚开始执行的时候,jvm会分配一把偏向锁(markword标记一下线程ID),偏向这个方法,会哭的娃娃有奶吃,cpu重点照顾下这个方法,如果在4秒内出现了其他同类型的synchronized方法,线程来争用当前持有锁对象的锁,好的,进行锁升级,升级为自旋锁,自旋锁也就是开启一个轮询盯着当前持有锁对象是否已执行完,当轮询10次以后,这个方法还是没有释放锁对象,好吧,再次进行锁升级,升级为重量级锁,交给操作系统OS进行锁维护,其实在jdk1.5版本之前synchronized锁粒度较大,上来就重量级锁,性能极差。后来进行了优化后,其实性能和atomic已经相当,甚至要优于他们,因为atomic技术使用的是自旋锁,而synchronized默认是偏向锁,更加轻量级,以上是对synchronized的底层原理设计的一些看法。

锁应用场景:

自旋锁:加锁代码执行时间短,线程数比较少(1-2),因为系统不允许大量自旋锁一直消耗cpu资源进行自旋

系统锁(重量级锁): 加锁代码执行时间较长,线程数比较多(>=3),

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力终会有回报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值