直接上正文
加锁方式:
同步实例方法:锁的是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时才能去竞争锁资源
本文详细解析了Java中synchronized关键字的底层实现原理,包括如何使用JVM内置锁及Monitor对象进行方法和代码块同步,解释了重入锁机制如何避免死锁,以及在实际代码中的应用案例。
2638

被折叠的 条评论
为什么被折叠?



