5、多线程锁
2024/12/19大约 6 分钟
5、多线程锁
1、synchronized
synchronized实现同步的基础:java中的每一个对象都可以作为锁
具体表现为以下几种形式:
对于普通同步方法,锁是当前实例对象
public synchronized void test(){}
对于静态同步方法,锁是当前类的Class对象
public static synchronized void test(){}
对于同步方法块,锁是Synchronized括号里配置的对象
synchronized(a){}
2、公平锁与非公平锁
package com.hjc.day03;
import java.util.concurrent.locks.ReentrantLock;
public class FairLock {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
//ticket.synSale();
ticket.saleReetLock();
}
}, "AA").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
//ticket.synSale();
ticket.saleReetLock();
}
}, "BB").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
//ticket.synSale();
ticket.saleReetLock();
}
}, "CC").start();
}
}
class Ticket {
private Integer num = 30;
//无参构造就是非公平锁,有参构造穿true是公平锁,传入false就是非公平锁
//非公平锁容易造成线程饿死,即有线程不会执行
//公平锁所有线程都会争抢,但是效率相对较低
//默认非公平锁
private final ReentrantLock reentrantLock = new ReentrantLock();
//非公平锁:效率相对较高,但是会造成线程饿死,即线程不执行
//private final ReentrantLock reentrantLock = new ReentrantLock(false);
//公平锁:效率底
//private final ReentrantLock reentrantLock = new ReentrantLock(true);
public synchronized void synSale() {
if(num > 0){
System.out.println(Thread.currentThread().getName() + "卖出" + (num--) + " 剩余" + num);
}
}
public void saleReetLock() {
reentrantLock.lock();
try {
if(num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出" + (num--) + " 剩余" + num);
}
} finally {
reentrantLock.unlock();
}
}
}
lock非公平锁结果
AA卖出30 剩余29
AA卖出29 剩余28
AA卖出28 剩余27
AA卖出27 剩余26
AA卖出26 剩余25
AA卖出25 剩余24
AA卖出24 剩余23
AA卖出23 剩余22
AA卖出22 剩余21
AA卖出21 剩余20
AA卖出20 剩余19
AA卖出19 剩余18
AA卖出18 剩余17
AA卖出17 剩余16
AA卖出16 剩余15
AA卖出15 剩余14
AA卖出14 剩余13
AA卖出13 剩余12
AA卖出12 剩余11
AA卖出11 剩余10
AA卖出10 剩余9
AA卖出9 剩余8
AA卖出8 剩余7
AA卖出7 剩余6
AA卖出6 剩余5
AA卖出5 剩余4
AA卖出4 剩余3
AA卖出3 剩余2
AA卖出2 剩余1
AA卖出1 剩余0
lock公平锁结果
AA卖出30 剩余29
AA卖出29 剩余28
BB卖出28 剩余27
CC卖出27 剩余26
AA卖出26 剩余25
BB卖出25 剩余24
CC卖出24 剩余23
AA卖出23 剩余22
BB卖出22 剩余21
CC卖出21 剩余20
AA卖出20 剩余19
BB卖出19 剩余18
CC卖出18 剩余17
AA卖出17 剩余16
BB卖出16 剩余15
CC卖出15 剩余14
AA卖出14 剩余13
BB卖出13 剩余12
CC卖出12 剩余11
AA卖出11 剩余10
BB卖出10 剩余9
CC卖出9 剩余8
AA卖出8 剩余7
BB卖出7 剩余6
CC卖出6 剩余5
AA卖出5 剩余4
BB卖出4 剩余3
CC卖出3 剩余2
AA卖出2 剩余1
BB卖出1 剩余0
3、可重入锁
synchronized和lock锁都是可重入锁,sunchronized是隐式的可重入锁,lock式显式(手动上锁解锁)的可重入锁
public static void main(String[] args) {
new Thread(() -> {
synchronized (Object.class){
synchronized (Object.class){
System.out.println("aaa");
}
}
}, "AA").start();
}
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock(true);
new Thread(() -> {
reentrantLock.lock();
reentrantLock.lock();
System.out.println("aaa");
reentrantLock.unlock();
reentrantLock.unlock();
}, "AA").start();
}
4、死锁
1、什么是死锁
两个或两个以上的进程在执行过程中,因为争夺资源而造成一种湘湖等待的现象,如果没有外力干涉,他们无法在执行下去
2、产生死锁的原因
第一 系统资源不足
第二 进程运行推进顺序不合适
第三 资源分配不当
3、演示死锁
1、synchronized死锁
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
new Thread(()->{
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"获取到o1");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"获取到o2");
}
}
},"AAA").start();
new Thread(()->{
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"获取到o2");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"获取到o1");
}
}
},"BBB").start();
}
}
2、lock死锁
public static void main(String[] args) {
ReentrantLock o1 = new ReentrantLock();
ReentrantLock o2 = new ReentrantLock();
new Thread(()->{
o1.lock();
System.out.println(Thread.currentThread().getName()+"获取到o1");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
o2.lock();
System.out.println(Thread.currentThread().getName()+"获取到o2");
o2.unlock();
o1.unlock();
},"AAA").start();
new Thread(()->{
o2.lock();
System.out.println(Thread.currentThread().getName()+"获取到o2");
o1.lock();
System.out.println(Thread.currentThread().getName()+"获取到o1");
o1.unlock();
o2.unlock();
},"BBB").start();
}
4、验证是否是死锁
1、jps命令,查询当前正在运行的java进程(jps -l)
2、jstack命令,jvm中自带的堆栈的跟踪工具 (jstack 进程号)
PS E:\core\study_juc\one> jps -l
3092 sun.tools.jps.Jps
13560 org.jetbrains.idea.maven.server.RemoteMavenServer36
13592 org.jetbrains.jps.cmdline.Launcher
13948
1884 com.hjc.day03.FairLock
PS E:\core\study_juc\one> jstack 1884
2024-01-10 21:59:59
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.301-b09 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x000001b9eda2c000 nid=0x1b6c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"BBB" #13 prio=5 os_prio=0 tid=0x000001b985d17000 nid=0x4470 waiting on condition [0x0000008dcd8fe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d62230d8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at com.hjc.day03.FairLock.lambda$main$1(FairLock.java:30)
at com.hjc.day03.FairLock$$Lambda$2/2093631819.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"AAA" #12 prio=5 os_prio=0 tid=0x000001b985d14800 nid=0x10c8 waiting on condition [0x0000008dcd7ff000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d6223108> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at com.hjc.day03.FairLock.lambda$main$0(FairLock.java:20)
at com.hjc.day03.FairLock$$Lambda$1/558638686.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000001b985a6b000 nid=0xeec runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000001b9859d2000 nid=0x338 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000001b9859ca000 nid=0x3ba8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000001b984766000 nid=0x3cc8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000001b9859bc000 nid=0x2b00 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000001b9859ba800 nid=0xcb0 runnable [0x0000008dcd0fe000]
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 <0x00000000d6119c08> (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 <0x00000000d6119c08> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:53)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000001b9846f7800 nid=0x18d0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000001b9846dd800 nid=0x2a54 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000001b9846c4000 nid=0x29d0 in Object.wait() [0x0000008dccdff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5f88ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000000d5f88ee0> (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=0x000001b9846bc800 nid=0x467c in Object.wait() [0x0000008dcccff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5f86c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000d5f86c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000001b984693800 nid=0x1084 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000001b9eda42800 nid=0x32a0 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000001b9eda43800 nid=0x4764 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000001b9eda45000 nid=0x3a7c runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000001b9eda46800 nid=0x2a40 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000001b9eda49800 nid=0x2e34 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x000001b9eda4a800 nid=0x1208 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000001b9eda4d800 nid=0x2de4 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000001b9eda4f000 nid=0x12dc runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000001b985acb000 nid=0x2f80 waiting on condition
JNI global references: 316
Found one Java-level deadlock:
=============================
//这部分可以看出死锁
"BBB":
waiting for ownable synchronizer 0x00000000d62230d8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "AAA"
"AAA":
waiting for ownable synchronizer 0x00000000d6223108, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "BBB"
Java stack information for the threads listed above:
===================================================
"BBB":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d62230d8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at com.hjc.day03.FairLock.lambda$main$0(FairLock.java:20)
at com.hjc.day03.FairLock$$Lambda$1/558638686.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
//发现一个死锁
Found 1 deadlock.