锁的八个问题
探讨两个问题:synchronized用的是否是同一把锁?以及锁的范围是什么?
class Phone {
public static synchronized void sendSMS() throws Exception {
//停留 4 秒
TimeUnit.SECONDS.sleep(4);
System.out.println("------sendSMS");
}
public synchronized void sendEmail() throws Exception {
System.out.println("------sendEmail");
}
public void getHello() {
System.out.println("------getHello");
}
}
/**
* @Description: 8 锁
*
1 标准访问,先打印短信还是邮件
------sendSMS
------sendEmail
2 停 4 秒在短信方法内,先打印短信还是邮件
------sendSMS
------sendEmail
3 新增普通的 hello 方法,是先打短信还是 hello
------getHello
------sendSMS
4 现在有两部手机,先打印短信还是邮件
------sendEmail
------sendSMS
5 两个静态同步方法,1 部手机,先打印短信还是邮件
------sendSMS
------sendEmai
6 两个静态同步方法,2 部手机,先打印短信还是邮件
------sendSMS
------sendEmail
7 1 个静态同步方法,1 个普通同步方法,1 部手机,先打印短信还是邮件
------sendEmail
------sendSMS
8 1 个静态同步方法,1 个普通同步方法,2 部手机,先打印短信还是邮件
------sendEmail
------sendSMS
分析:1,2是因为手握同一把锁this,所以是先后执行。3是hello方法并未加锁,而sms睡了4s,则hello方法先打印
4是因为两个手机,两个方法手握两把锁,所以先输出email;5,6手握的Class字节码对象,是同一把锁,所以先打印sms,7,8不是一把锁且范围不一样,类似大楼和房间
结论: 一个对象里面如果有多个 synchronized 方法,某一个时刻内,只要一个线程去调用其中的 一个 synchronized 方法了, 其它的线程都只能等待
公平锁和非公平锁
private final ReentrantLock lock = new ReentrantLock(true);
---------------------------------------
private final ReentrantLock lock = new ReentrantLock(false);
公平锁:会造成其他线程被饿死的情况,效率高
非公平锁:其他线程不会出现饿死现象,效率低
Re的无参构造和有参构造
public ReentrantLock() {
sync = new NonfairSync();
}
//无参构造会默认生产非公平锁
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//有参构造会根据传入的布尔值来生产公平或非公平锁
公平锁源码
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; /** * Acquires only if reentrant or queue is empty. */ final boolean initialTryLock() { Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedThreads() && compareAndSetState(0, 1)) { setExclusiveOwnerThread(current); return true; } } else if (getExclusiveOwnerThread() == current) { if (++c < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(c); return true; } return false; } //公平锁会先进行判断,这是其实现公平的原因,也是其效率低的原因
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没有释放而阻塞.
- synchronized和lock都是可重入锁;sychronized是隐式锁,不用手工上锁与解锁,而lock为显示锁,需要手工上锁与解锁。
代码验证synchronized和ReentrantLock是可重入锁:
Object o = new Object();
new Thread(()->{
synchronized(o) {
System.out.println(Thread.currentThread().getName()+" 外层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 中层");
synchronized (o) {
System.out.println(Thread.currentThread().getName()+" 内层");
}
}
}
},"t1").start();
=====================================
调用线程:三个打印都会输出
public class SyncLockDemo {
public synchronized void add() {
add();
}
public static void main(String[] args) {
//Lock演示可重入锁
Lock lock = new ReentrantLock();
//创建线程
new Thread(()->{
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName()+" 外层");
try {
//上锁
lock.lock();
System.out.println(Thread.currentThread().getName()+" 内层");
}finally {
//释放锁
//若注释掉此行,则"aaaa"就不会再执行,因为当前线程的锁尚未释放
lock.unlock();
}
}finally {
//释放做
lock.unlock();
}
},"t1").start();
//创建新线程
new Thread(()->{
lock.lock();
System.out.println("aaaa");
lock.unlock();
},"aa").start();
}
}
=================================
因为两个线程使用的是同一把锁,若线程1没有unlock,则线程2就会一直等待其锁的释放
⑤. Synchronized的重入的实现机理 (为什么任何一个对象都可以成为一个锁)
- 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
- 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将计数器加1
- 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程时当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直到持有线程释放该锁
- 当执行monitorexit,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已经释放
死锁
什么是死锁
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去
如果资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁
线程A手握锁A,线程B手握锁B,但是线程A却想要获取锁B,线程B却想要获取锁A。但是线程A必须获取锁B后才会释放锁A让线程B获取,但线程B不会释放锁B,因为它还没获取锁A。这样就会出现一直僵持的情况
public class DeadLockDemo{
static Object lockA = new Object();
static Object lockB = new Object();
public static void main(String[] args){
Thread a = new Thread(() -> {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "\t" + " 自己持有A锁,期待获得B锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "\t 获得B锁成功");
}
}
}, "a");
a.start();
new Thread(() -> {
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"\t"+" 自己持有B锁,期待获得A锁");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"\t 获得A锁成功");
}
}
},"b").start();
}
}
产生死锁原因
- 系统资源不足
- 进程运行推进的顺序不合适
-
验证是否是死锁
jps 查看进程号
- jstack jvm自带的堆栈跟踪工具 输出详细信息 ```bash Microsoft Windows [版本 10.0.19043.1165] (c) Microsoft Corporation。保留所有权利。
C:\Windows\system32>jps 24792 Jps
C:\Windows\system32>jps 9456 16228 Jps 9252 RemoteMavenServer36 21704 Launcher 5756 DeadLockDemo
C:\Windows\system32>jstack 5756 2021-09-20 09:48:51 Full thread dump OpenJDK 64-Bit Server VM (25.302-b08 mixed mode):
“DestroyJavaVM” #14 prio=5 os_prio=0 tid=0x000001b817f4b000 nid=0x5c58 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“b” #13 prio=5 os_prio=0 tid=0x000001b834f97000 nid=0x4f38 waiting for monitor entry [0x0000007d645ff000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadLockDemo.lambda$main$1(DeadLockDemo.java:38)
- waiting to lock <0x000000076ba87dc0> (a java.lang.Object)
- locked <0x000000076ba87dd0> (a java.lang.Object)
at DeadLockDemo$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
“a” #12 prio=5 os_prio=0 tid=0x000001b834f94800 nid=0x37dc waiting for monitor entry [0x0000007d644ff000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadLockDemo.lambda$main$0(DeadLockDemo.java:23)
- waiting to lock <0x000000076ba87dd0> (a java.lang.Object)
- locked <0x000000076ba87dc0> (a java.lang.Object)
at DeadLockDemo$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
“Service Thread” #11 daemon prio=9 os_prio=0 tid=0x000001b834bf2800 nid=0x1e1c runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“C1 CompilerThread3” #10 daemon prio=9 os_prio=2 tid=0x000001b834bed000 nid=0x31e0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“C2 CompilerThread2” #9 daemon prio=9 os_prio=2 tid=0x000001b834bea000 nid=0x6254 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“C2 CompilerThread1” #8 daemon prio=9 os_prio=2 tid=0x000001b834be9800 nid=0x2d5c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“C2 CompilerThread0” #7 daemon prio=9 os_prio=2 tid=0x000001b834be6800 nid=0x2b74 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“Monitor Ctrl-Break” #6 daemon prio=5 os_prio=0 tid=0x000001b834be0800 nid=0x224c runnable [0x0000007d63dfe000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076bbc73f0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076bbc73f0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:49)
“Attach Listener” #5 daemon prio=5 os_prio=2 tid=0x000001b8338c3000 nid=0x54b0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“Signal Dispatcher” #4 daemon prio=9 os_prio=2 tid=0x000001b8338c0800 nid=0x48ec runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
“Finalizer” #3 daemon prio=8 os_prio=1 tid=0x000001b83383b800 nid=0x2db0 in Object.wait() [0x0000007d63aff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b909508> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076b909508> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
“Reference Handler” #2 daemon prio=10 os_prio=2 tid=0x000001b833834000 nid=0x2d8 in Object.wait() [0x0000007d639ff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b907118> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076b907118> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
“VM Thread” os_prio=2 tid=0x000001b833802800 nid=0x4918 runnable
“GC task thread#0 (ParallelGC)” os_prio=0 tid=0x000001b817f60800 nid=0x52b0 runnable
“GC task thread#1 (ParallelGC)” os_prio=0 tid=0x000001b817f62800 nid=0x4d0c runnable
“GC task thread#2 (ParallelGC)” os_prio=0 tid=0x000001b817f64000 nid=0x25d8 runnable
“GC task thread#3 (ParallelGC)” os_prio=0 tid=0x000001b817f65800 nid=0x2a98 runnable
“GC task thread#4 (ParallelGC)” os_prio=0 tid=0x000001b817f69000 nid=0x5828 runnable
“GC task thread#5 (ParallelGC)” os_prio=0 tid=0x000001b817f6a800 nid=0x5f0c runnable
“GC task thread#6 (ParallelGC)” os_prio=0 tid=0x000001b817f6d800 nid=0x4e18 runnable
“GC task thread#7 (ParallelGC)” os_prio=0 tid=0x000001b817f6f000 nid=0x63a0 runnable
“VM Periodic Task Thread” os_prio=2 tid=0x000001b834bf5800 nid=0x3b98 waiting on condition
JNI global references: 317
Found one Java-level deadlock:
“b”: waiting to lock monitor 0x000001b83383a758 (object 0x000000076ba87dc0, a java.lang.Object), which is held by “a” “a”: waiting to lock monitor 0x000001b8338380d8 (object 0x000000076ba87dd0, a java.lang.Object), which is held by “b”
Java stack information for the threads listed above:
“b”: at DeadLockDemo.lambda$main$1(DeadLockDemo.java:38)
- waiting to lock <0x000000076ba87dc0> (a java.lang.Object)
- locked <0x000000076ba87dd0> (a java.lang.Object)
at DeadLockDemo$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
“a”: at DeadLockDemo.lambda$main$0(DeadLockDemo.java:23)
- waiting to lock <0x000000076ba87dd0> (a java.lang.Object)
- locked <0x000000076ba87dc0> (a java.lang.Object)
at DeadLockDemo$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
C:\Windows\system32> ```