synchronized重入锁分析

本文详细解析了Java中synchronized关键字的底层实现原理,包括如何使用JVM内置锁及Monitor对象进行方法和代码块同步,解释了重入锁机制如何避免死锁,以及在实际代码中的应用案例。

直接上正文

加锁方式:

同步实例方法:锁的是this 当前调用对象

同步代码块:加锁的对象是()里的实例

同步类方法:锁的是当前类对象,Class对象

底层原理:JVM内置锁通过synchronized使用,通过内部对象Monitor(监视器锁)实现,通过进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层的操作系统Mutex lock(互斥锁)实现,是一个重量锁性能较低

Unsafe.getUnsafe().monitorEnter(obj); // 手动加锁
Unsafe.getUnsafe().monitorExit(obj);  // 手动解锁

如果A是一个锁对象 那么在它内部有一个ObjectMonitor 里面会记录

_count:线程加锁了几次 加一次就+1 释放一次就减1

_owner:持有当前Monitor对象的是那个线程,就是加锁成功的那个线程

_WaitSet:存放了正在等待状态的线程,如果一旦线程T1结束释放了monitorExit 那么T1会去通知等待的线程去抢CPU的使用权

T2如果抢到了那么就去执行T1的逻辑

 

铺垫完成

public class A {
    public synchronized void test1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+":进入test1");
        Thread.sleep(10000L);
        System.out.println(Thread.currentThread().getName()+":等待10秒");
        System.out.println(Thread.currentThread().getName()+":退出test1");
    }

    public synchronized void test2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+":进入test2");
        Thread.sleep(10000L);
        System.out.println(Thread.currentThread().getName()+":等待10秒");
        System.out.println(Thread.currentThread().getName()+":退出test2");
    }

    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        Thread thread = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                a.test1();
            }
        });
        Thread.sleep(200L);
        Thread thread2 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                a.test2();
            }
        });
        thread.start();
        thread2.start();
    }
}

Thread-0:进入test1
Thread-0:等待10秒
Thread-0:退出test1
Thread-1:进入test2
Thread-1:等待10秒
Thread-1:退出test2

运行结果表示:共同持有了一个锁对象 this 

基于上面案例的分析:只有thread释放了锁,thread2才能去获取锁 因为使用了同一个锁对象 this

public class A {
    public synchronized void test1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+":进入test1");
        System.out.println(Thread.currentThread().getName()+":退出test1");
        test2();
    }

    public synchronized void test2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+":进入test2");
        Thread.sleep(10000L);
        System.out.println(Thread.currentThread().getName()+":等待10秒");
        System.out.println(Thread.currentThread().getName()+":退出test2");
    }

    public static void main(String[] args) throws Exception {
        A a = new A();
        Thread thread = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                a.test1();
            }
        });
        Thread.sleep(200L);
        Thread thread2 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                a.test2();
            }
        });
        thread.start();
        thread2.start();
    }
}

Thread-0:进入test1
Thread-0:退出test1
Thread-0:进入test2
Thread-0:等待10秒
Thread-0:退出test2
Thread-1:进入test2
Thread-1:等待10秒
Thread-1:退出test2

按照之前的分析 Thread-0进入test1,但是test1方法里调用test2. 这个时候test1还没有释放锁,那么就根本不可能获取到test2的锁。这个时候发生死锁才对。但是结果是并没有。

是因为synchronized的重入锁,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的,Thread-1只能在外面等待Thread-0释放test2上的锁和test1上的锁,_count计数器变为0时才能去竞争锁资源

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值