一、锁细化
一间大屋子有两个功能:睡觉、学习,互不相干。
现在小明要学习,小红要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低
public class TestMultiLock {
public static void main(String[] args) {
BigRoom bigRoom = new BigRoom();
new Thread(() -> {
try {
bigRoom.study();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"小明").start();
new Thread(() -> {
try {
bigRoom.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"小红").start();
}
}
@Slf4j
class BigRoom {
public void sleep() throws InterruptedException {
synchronized (this) {
log.debug("睡 2 小时");
Thread.sleep(2000);
}
}
public void study() throws InterruptedException {
synchronized (this) {
log.debug("学 1 小时");
Thread.sleep(1000);
}
}
}
18:38:46.400 [小明] DEBUG juc.lock.BigRoom - 学 1 小时
18:38:47.403 [小红] DEBUG juc.lock.BigRoom - 睡 2 小时
解决方法:准备多个房间(多个对象锁)
public class TestMultiLock {
public static void main(String[] args) {
BigRoom bigRoom = new BigRoom();
new Thread(() -> {
try {
bigRoom.study();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"小明").start();
new Thread(() -> {
try {
bigRoom.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"小红").start();
}
}
@Slf4j
class BigRoom {
private final Object studyRoom = new Object();
private final Object bedroom = new Object();
public void sleep() throws InterruptedException {
synchronized (bedroom) {
log.debug("睡 2 小时");
Thread.sleep(2000);
}
}
public void study() throws InterruptedException {
synchronized (studyRoom) {
log.debug("学 1 小时");
Thread.sleep(1000);
}
}
}
18:39:46.729 [小明] DEBUG juc.lock.BigRoom - 学 1 小时
18:39:46.729 [小红] DEBUG juc.lock.BigRoom - 睡 2 小时
将锁的粒度细化:
- 好处:可以增强并发度
- 坏处:如果一个线程需要同时获得多把锁,容易发生死锁
二、死锁
一个线程需要同时获取多把锁,这时可能发生死锁:
- t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
- t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁
@Slf4j
public class DeadLock {
public static void main(String[] args) {
test1();
}
private static void test1() {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
log.debug("lock A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
log.debug("lock B");
log.debug("操作...");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
log.debug("lock B");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
log.debug("lock A");
log.debug("操作...");
}
}
}, "t2");
t1.start();
t2.start();
}
}
19:06:26.007 [t2] DEBUG juc.lock.DeadLock - lock B
19:06:26.007 [t1] DEBUG juc.lock.DeadLock - lock A
定位死锁
- 使用 jconsole工具
- 使用 jps 定位进程 id,再用 jstack 定位死锁
hongcaixia@hongcaixiadeMacBook-Pro thread % jps
4146 Launcher
4147 DeadLock
46231
30200 TestProducerConsumer
46764 Launcher
5070 Jps
hongcaixia@hongcaixiadeMacBook-Pro thread % jstack 4147
"t2" #12 prio=5 os_prio=31 tid=0x00007fe0a7882800 nid=0x7e03 waiting for monitor entry [0x00007000062ef000]
java.lang.Thread.State: BLOCKED (on object monitor)
at juc.lock.DeadLock.lambda$test1$1(DeadLock.java:47)
- waiting to lock <0x0000000716059778> (a java.lang.Object)
- locked <0x0000000716059788> (a java.lang.Object)
at juc.lock.DeadLock$$Lambda$2/1212899836.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"t1" #11 prio=5 os_prio=31 tid=0x00007fe0b007c800 nid=0x5703 waiting for monitor entry [0x00007000061ec000]
java.lang.Thread.State: BLOCKED (on object monitor)
at juc.lock.DeadLock.lambda$test1$0(DeadLock.java:32)
- waiting to lock <0x0000000716059788> (a java.lang.Object)
- locked <0x0000000716059778> (a java.lang.Object)
at juc.lock.DeadLock$$Lambda$1/997608398.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found one Java-level deadlock:
=============================
"t2":
waiting to lock monitor 0x00007fe0a700bcd8 (object 0x0000000716059778, a java.lang.Object),
which is held by "t1"
"t1":
waiting to lock monitor 0x00007fe0a700df38 (object 0x0000000716059788, a java.lang.Object),
which is held by "t2"
哲学家就餐问题
有五位哲学家,围坐在圆桌旁。
他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。
吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
如果筷子被身边的人拿着,自己就得等待.
public class PhilosopherMeal {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c5, c1).start();
}
}
/**
* 哲学家类
*/
@Slf4j
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true) {
// 尝试获得左手筷子
synchronized (left) {
// 尝试获得右手筷子
synchronized (right) {
eat();
}
}
}
}
Random random = new Random();
private void eat(){
log.debug("eating...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 筷子类
*/
class Chopstick {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{" + name + '}';
}
}
19:21:52.361 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:52.361 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:53.368 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:53.369 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:54.371 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:54.371 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:21:55.372 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:56.376 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:57.376 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:21:58.379 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
检查死锁:
hongcaixia@hongcaixiadeMacBook-Pro thread % jps
46231
30200 TestProducerConsumer
11948 Launcher
46764 Launcher
11949 PhilosopherMeal
13101 Jps
hongcaixia@hongcaixiadeMacBook-Pro thread % jstack 11949
Found one Java-level deadlock:
=============================
"阿基米德":
waiting to lock monitor 0x00007fbc430cf8b8 (object 0x00000007156f92b0, a juc.lock.Chopstick),
which is held by "苏格拉底"
"苏格拉底":
waiting to lock monitor 0x00007fbc430ccc08 (object 0x00000007156f92f0, a juc.lock.Chopstick),
which is held by "柏拉图"
"柏拉图":
waiting to lock monitor 0x00007fbc430ccb58 (object 0x00000007156f9330, a juc.lock.Chopstick),
which is held by "亚里士多德"
"亚里士多德":
waiting to lock monitor 0x00007fbc430cf808 (object 0x00000007156f9370, a juc.lock.Chopstick),
which is held by "赫拉克利特"
"赫拉克利特":
waiting to lock monitor 0x00007fbc430cf758 (object 0x00000007156f93b0, a juc.lock.Chopstick),
which is held by "阿基米德"
Java stack information for the threads listed above:
===================================================
"阿基米德":
at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
- waiting to lock <0x00000007156f92b0> (a juc.lock.Chopstick)
- locked <0x00000007156f93b0> (a juc.lock.Chopstick)
"苏格拉底":
at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
- waiting to lock <0x00000007156f92f0> (a juc.lock.Chopstick)
- locked <0x00000007156f92b0> (a juc.lock.Chopstick)
"柏拉图":
at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
- waiting to lock <0x00000007156f9330> (a juc.lock.Chopstick)
- locked <0x00000007156f92f0> (a juc.lock.Chopstick)
"亚里士��德":
at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
- waiting to lock <0x00000007156f9370> (a juc.lock.Chopstick)
- locked <0x00000007156f9330> (a juc.lock.Chopstick)
"赫拉克利特":
at juc.lock.Philosopher.run(PhilosopherMeal.java:53)
- waiting to lock <0x00000007156f93b0> (a juc.lock.Chopstick)
- locked <0x00000007156f9370> (a juc.lock.Chopstick)
Found 1 deadlock.
避免死锁
顺序加锁,每个线程都按照相同的顺序拿锁
解决哲学家就餐问题:让他们按照顺序拿锁:
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c1, c5).start();
}
如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 linux 下可以通过 top 先定位到CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack 排查
三、活锁
两个线程互相改变对方的结束条件,最后谁也无法结束
@Slf4j
public class LiveLock {
static volatile int count = 10;
public static void main(String[] args) {
new Thread(() -> {
// 期望减到 0 退出循环
while (count > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
log.debug("count: {}", count);
}
}, "t1").start();
new Thread(() -> {
// 期望超过 20 退出循环
while (count < 20) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
log.debug("count: {}", count);
}
}, "t2").start();
}
}
19:32:44.468 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.468 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:44.675 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:44.675 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.877 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:44.877 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:45.082 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:45.082 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.286 [t1] DEBUG juc.lock.LiveLock - count: 8
19:32:45.286 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.490 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:45.490 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:45.693 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:45.693 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:45.896 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:45.896 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.098 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.098 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.301 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.301 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.504 [t1] DEBUG juc.lock.LiveLock - count: 10
19:32:46.504 [t2] DEBUG juc.lock.LiveLock - count: 11
19:32:46.706 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:46.706 [t2] DEBUG juc.lock.LiveLock - count: 10
19:32:46.908 [t1] DEBUG juc.lock.LiveLock - count: 9
19:32:46.908 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:47.113 [t2] DEBUG juc.lock.LiveLock - count: 9
19:32:47.113 [t1] DEBUG juc.lock.LiveLock - count: 8
19:32:47.318 [t2] DEBUG juc.lock.LiveLock - count: 10
···
解决:让两个线程运行时间错开,增加随机的睡眠时间
四、饥饿
一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束。
针对哲学家就餐问题使用顺序拿锁,其中大部分时间都是赫拉克利特和苏格拉底拿到锁,而阿基米德一直没机会拿到锁,没机会执行。
19:43:41.832 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:42.836 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:42.836 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:43.837 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:43.837 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:44.838 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:44.838 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:45.838 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:45.838 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:46.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:46.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:47.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:47.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:48.839 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:48.839 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:49.841 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:49.841 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:50.844 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:50.844 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:51.846 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:51.846 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:52.847 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:52.847 [亚里士多德] DEBUG juc.lock.Philosopher - eating...
19:43:53.847 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:53.847 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:54.852 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:54.852 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:55.857 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:55.857 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:56.860 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:56.860 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:57.864 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:57.864 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:58.869 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:58.869 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:43:59.875 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:43:59.875 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:00.875 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:00.875 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:01.880 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:01.880 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:02.883 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:02.883 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:03.886 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:03.886 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:04.886 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:04.886 [苏格拉底] DEBUG juc.lock.Philosopher - eating...
19:44:05.890 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:06.891 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:07.897 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:08.897 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:09.901 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:10.905 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:11.907 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:12.912 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:13.917 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:14.919 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:15.922 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:16.924 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:17.928 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:18.931 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
19:44:19.934 [赫拉克利特] DEBUG juc.lock.Philosopher - eating...
···
网友评论