目录
1、一个synchronized修饰的方法调用另一个synchronized修饰的方法,可以吗?
5、ReentrantLock与Synchronized对比
1、一个synchronized修饰的方法调用另一个synchronized修饰的方法,可以吗?
如果答案是否定的,不能实现这样的调用,那么产生的效果就是,某线程获得了锁并执行方法1的过程中,又要去获得锁以便执行被方法1调用的方法2,方法2 的锁由于已经被执行方法1的线程占用,所以拿不到锁,然后方法1也因此无法继续执行完毕从而线程无法释放锁,于是死锁产生了。
具体看一下效果。
同一个类的两个方法m1和m2都被synchronized关键字修饰,相当于都是对当前对象加锁。
/**
* 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
* 也就是说synchronized获得的锁是可重入的
*/
package main.java.basic._synchronized;
import java.util.concurrent.TimeUnit;
public class SynCallSyn1 {
synchronized void m1() {
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2");
}
public static void main(String[] args) {
SynCallSyn1 s=new SynCallSyn1();
new Thread(()->s.m1()).start();
}
}
执行结果:
m1 start
m2
m1 end
综上,synchronized获得锁是可重入的,一个线程获得某个对象的锁,再次申请时仍然会得到该对象的锁。
2、可重入的概念
由此引出了可重入的概念:
可重入就是说某个线程已经获得某个锁,可以再次获取该锁而不会出现死锁。加锁是对同一对象加锁,但是可以加很多次,并且可以释放对应次。
3、可重入锁的两种实现方式
可重入锁的实现方式有两种,一种是上面的synchronized,另一种是ReentrantLock。
(1)方式1 :synchronized
package main.java.basic.reentrantLock;
// 演示可重入锁是什么意思,可重入,就是可以重复获取相同的锁,synchronized和ReentrantLock都是可重入的
// 可重入降低了编程复杂性
public class WhatReentrant {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
System.out.println("第1次获取锁,这个锁是:" + this);
int index = 1;
while (true) {
synchronized (this) {
System.out.println("第" + (++index) + "次获取锁,这个锁是:" + this);
}
if (index == 10) {
break;
}
}
}
}
}).start();
}
}
执行结果:
第1次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第2次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第3次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第4次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第5次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第6次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第7次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第8次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第9次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
第10次获取锁,这个锁是:main.java.basic.reentrantLock.WhatReentrant$1@7cfcbbed
(2)方式2:ReentrantLock
package main.java.basic.reentrantLock;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
public class WhatReentrant2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("第1次获取锁,这个锁是:" + lock);
int index = 1;
while (true) {
try {
lock.lock();
System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);
try {
Thread.sleep(new Random().nextInt(200));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (index == 10) {
break;
}
} finally {
lock.unlock();//释放锁
}
}
} finally {
lock.unlock();
}
}
}).start();
}
}
执行结果:
第1次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第2次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第3次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第4次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第5次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第6次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第7次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第8次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第9次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
第10次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@59fae87e[Locked by thread Thread-0]
4、注意事项
需要注意的是ReentrantLock加锁是有加锁次数计数的,当前线程主动加锁的次数必须要在主动释放对应次数的锁之后,其他对象才能继续使用ReentrantLock对象加锁。另外,从上面代码容易看出,ReentrantLock对象加锁和释放锁都需要主动进行操作。
5、ReentrantLock与Synchronized对比
(1)可重入性
区别不大
(2)锁的实现
前者是JDK实现(java代码),后者是JVM实现(偏向于系统层面的实现)
(3)性能的区别
自从Synchronized引入偏向锁、轻量级锁后,两者性能相近,官方建议用后者,写法更容易。
(4)功能区别
前者需要主动声明加锁和释放锁,后者不用。锁的细粒度和灵活度方面,前者更优。
ReentrantLock可以指定是公平锁还是非公平锁;Synchronized是非公平锁,公平锁的意思是先等待的线程先获得锁。ReentrantLock提供了一个Condition类,可以分组唤醒需要唤醒的线程;Synchronized唤醒线程是随机唤醒或全部唤醒。ReentrantLock提供了中断等待获得锁的机制,lock.lockInterruptibly()
参考:https://blog.youkuaiyun.com/w8y56f/article/details/89554060