一、多把锁
多把不相干的锁
假如有一个房间,学习、睡觉互不相干。
现在A要学习,B要睡觉,但如果只用一间屋子(一个对象锁)的话,并发度很低。
public class Test1 {
public static void main(String[] args) {
Room room = new Room();
new Thread(() -> room.study(),"A").start();
new Thread(() -> room.sleep(),"B").start();
}
}
@Slf4j
class Room{
public void study(){
synchronized (this){
log.info("我要学习2小时...");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void sleep(){
synchronized (this){
log.info("我要睡觉1小时...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果:
21:34:51.180 INFO [A] com.dxf.活跃性.Room - 我要学习2小时...
21:34:53.180 INFO [B] com.dxf.活跃性.Room - 我要睡觉1小时...
通过执行时间我们可以看出,上述代码中两个线程竞争同一把锁,导致并发性很低。
使用多把锁:
public class Test1 {
public static void main(String[] args) {
Room room = new Room();
new Thread(() -> room.study(),"A").start();
new Thread(() -> room.sleep(),"B").start();
}
}
@Slf4j
class Room{
//此对象专门用来负责睡觉锁
private final Object bedRoom = new Object();
//此对象专门用来负责学习锁
private final Object studyRoom = new Object();
public void study(){
synchronized (studyRoom){
log.info("我要学习2小时...");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void sleep(){
synchronized (bedRoom){
log.info("我要睡觉1小时...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果:
21:37:20.500 INFO [A] com.dxf.活跃性.Room - 我要学习2小时...
21:37:20.500 INFO [B] com.dxf.活跃性.Room - 我要睡觉1小时...
通过执行时间我们可以看出A和B两个线程同时做两件不相干的事情,互不打扰,提高了并发。
缺点:容易出现死锁.
二、死锁
现象演示:
public class DeadLock {
static final Object objA = new Object();
static final Object objB = new Object();
public static void main(String[] args) {
new Thread(() ->{
synchronized (objA){
log.info("获得objA锁...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objB){
log.info("获得objB锁...");
}
}
},"A").start();
new Thread(() ->{
synchronized (objB){
log.info("获得objB锁...");
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objA){
log.info("获得objA锁...");
}
}
},"B").start();
}
}
输出结果:
21:48:10.961 INFO [A] com.dxf.活跃性.DeadLock - 获得objA锁...
21:48:10.961 INFO [B] com.dxf.活跃性.DeadLock - 获得objB锁...
我们发现上述代码A和B两个线程已经持有自己的锁后,又想获取对方的锁,而对方的锁没有释放掉,导致A和B两个线程互相进入等待状态,谁也获取不到,程序进入阻塞状态中不能继续执行.
1.定位死锁
- 在控制台使用jps命令查看正在运行的java进程
E:\work_space\thread-study>jps
3024 DeadLock
13716 Jps
14632 MessageQueue
17112
4056 JConsole
7532 Launcher
- 定位到当前项目的进程id,执行jstack id
E:\work_space\thread-study>jstack 3024
2021-01-17 22:00:39
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.251-b08 mixed mode):
"RMI TCP Connection(5)-192.168.109.1" #22 daemon prio=5 os_prio=0 tid=0x0000000020bf1800 nid=0x3290 in Object.wait() [0x0000000022bdc000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076c732670> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)
at com.sun.jmx.remote.internal.ArrayNotificationBuffer.fetchNotifications(ArrayNotificationBuffer.java:449)
- locked <0x000000076c732670> (a com.sun.jmx.remote.internal.ArrayNotificationBuffer)
at com.sun.jmx.remote.internal.ArrayNotificationBuffer$ShareBuffer.fetchNotifications(ArrayNotificationBuffer.java:227)
at com.sun.jmx.remote.internal.ServerNotifForwarder.fetchNotifs(ServerNotifForwarder.java:274)
at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1270)
at javax.management.remote.rmi.RMIConnectionImpl$4.run(RMIConnectionImpl.java:1268)
at javax.management.remote.rmi.RMIConnectionImpl.fetchNotifications(RMIConnectionImpl.java:1274)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$5/1216550589.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(4)-192.168.109.1" #21 daemon prio=5 os_prio=0 tid=0x000000001e9f6000 nid=0x3ad4 runnable [0x0000000021b0e000]
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 java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x000000076caf0a00> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$5/1216550589.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(3)-192.168.109.1" #20 daemon prio=5 os_prio=0 tid=0x000000001e82d800 nid=0x4654 runnable [0x0000000021a0e000]
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 java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x000000076c9a8f08> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$5/1216550589.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"JMX server connection timeout 18" #18 daemon prio=5 os_prio=0 tid=0x000000002092a800 nid=0x36c8 in Object.wait() [0x000000002180e000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076c3c1da8> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x000000076c3c1da8> (a [I)
at java.lang.Thread.run(Thread.java:748)
"RMI Scheduler(0)" #17 daemon prio=5 os_prio=0 tid=0x0000000020950800 nid=0x94c waiting on condition [0x000000002170f000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076c0aeba8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Accept-0" #15 daemon prio=5 os_prio=0 tid=0x000000002093a800 nid=0x429c runnable [0x000000002130f000]
java.lang.Thread.State: RUNNABLE
at java.net.DualStackPlainSocketImpl.accept0(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
- locked <0x000000076c0c0438> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
at java.lang.Thread.run(Thread.java:748)
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000003392800 nid=0x47a0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"B" #12 prio=5 os_prio=0 tid=0x000000001ea5d800 nid=0x2c60 waiting for monitor entry [0x000000002120f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.dxf.活跃性.DeadLock.lambda$main$1(DeadLock.java:57)
- waiting to lock <0x000000076b716bc0> (a java.lang.Object)
- locked <0x000000076b716bd0> (a java.lang.Object)
at com.dxf.活跃性.DeadLock$$Lambda$2/1830908236.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"A" #11 prio=5 os_prio=0 tid=0x000000001e7ee000 nid=0x3b50 waiting for monitor entry [0x000000002110f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.dxf.活跃性.DeadLock.lambda$main$0(DeadLock.java:37)
- waiting to lock <0x000000076b716bd0> (a java.lang.Object)
- locked <0x000000076b716bc0> (a java.lang.Object)
at com.dxf.活跃性.DeadLock$$Lambda$1/1972439101.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000001e7bf000 nid=0x2234 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001e790000 nid=0x34f4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001e742000 nid=0x2c08 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001e741000 nid=0x44bc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001e73b000 nid=0x1234 runnable [0x000000001ed6e000]
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 <0x000000076b187250> (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 <0x000000076b187250> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001cd15000 nid=0x2f28 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001cd1d800 nid=0x3950 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000348d800 nid=0x1bd4 in Object.wait() [0x000000001e66f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b008ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076b008ee0> (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=0x0000000003486800 nid=0x2130 in Object.wait() [0x000000001e56e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b006c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076b006c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001ccc7800 nid=0x1650 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000033a8800 nid=0xc28 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000033aa000 nid=0x35d4 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000033ab800 nid=0x330c runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000033ad000 nid=0x3598 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00000000033af800 nid=0x23c4 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00000000033b0800 nid=0x44f4 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001e7d9000 nid=0x36a4 waiting on condition
JNI global references: 339
Found one Java-level deadlock:
=============================
"B":
waiting to lock monitor 0x000000000348a778 (object 0x000000076b716bc0, a java.lang.Object),
which is held by "A"
"A":
waiting to lock monitor 0x000000000348cd48 (object 0x000000076b716bd0, a java.lang.Object),
which is held by "B"
Java stack information for the threads listed above:
===================================================
"B":
at com.dxf.活跃性.DeadLock.lambda$main$1(DeadLock.java:57)
- waiting to lock <0x000000076b716bc0> (a java.lang.Object)
- locked <0x000000076b716bd0> (a java.lang.Object)
at com.dxf.活跃性.DeadLock$$Lambda$2/1830908236.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"A":
at com.dxf.活跃性.DeadLock.lambda$main$0(DeadLock.java:37)
- waiting to lock <0x000000076b716bd0> (a java.lang.Object)
- locked <0x000000076b716bc0> (a java.lang.Object)
at com.dxf.活跃性.DeadLock$$Lambda$1/1972439101.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
可以在输出信息中看到死锁发生的位置以及对应的线程所等待的对象锁信息
-
使用jconsole查看
jconsole查看死锁信息01
jconsole查看死锁信息02
2.哲学家就餐
有5个哲学家吃饭,桌子上只有5根筷子,每个人需要持有两根筷子之后才可以吃饭.
有一种可能,当每个人都持有1根筷子的时候,那么谁也得不到第二根,就发生了死锁.
三、活锁
两个线程互相改变对方的结束条件,导致双方都不能结束.
public class LiveLock {
static int count = 10;
public static void main(String[] args) {
new Thread(() ->{
while (count > 0){
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
count --;
log.info(String.valueOf(count));
}
},"t1").start();
new Thread(() ->{
while (count < 20){
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
count ++;
log.info(String.valueOf(count));
}
},"t1").start();
}
}
解决办法:改变线程的睡眠时间,交错执行
四、饥饿
一个线程由于优先级太低,始终得不到cpu调度执行,也不能够结束.
- 死锁中交换加锁顺序,变成顺序加锁,则会出现线程饥饿的现象.
网友评论