使用多把锁提高并发性
// 一间大屋子有两个功能:睡觉,学习,互不相干
// 现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低
// 解决方法是准备多个房间(多个对象锁)
public class Main{
public static void main(String[] args) {
BigRoom bigRoom = new BigRoom();
new Thread(() -> { bigRoom.study(); },"小南").start();
new Thread(() -> { bigRoom.sleep(); },"小文").start();
}
}
@Slf4j
class BigRoom {
private final Object studyRoom = new Object();
private final Object bedRoom = new Object();
// sleep和study业务上没有关联
public void sleep() throws InterruptedException {
synchronized (bedRoom){
log.debug("睡2小时");
TimeUnit.SECONDS.sleep(2);
}
}
public void study() throws InterruptedException {
synchronized (studyRoom){
log.debug("学习1小时");
TimeUnit.SECONDS.sleep(1);
}
}
}
发生死锁
public static void main(String[] args) {
Object A = new Object();
Object B = new Object();
new Thread(() -> {
synchronized (A) {
log.debug("lock A");
synchronized (B) {
log.debug("lock B");
log.debug("操作...");
}
}
}, "t1").start();
new Thread(() -> {
synchronized (B) {
log.debug("lock B");
synchronized (A) {
log.debug("lock A");
log.debug("操作...");
}
}
}, "t2").start();
}
定位死锁
使用jstack
或者jconosle
工具进行定位
# jps查看运行的Java进程
PS C:\Users\hhx> jps -l
10744 jdk.jcmd/sun.tools.jps.Jps
15372 org.jetbrains.jps.cmdline.Launcher
2412 cn.study.Main
# 使用jstack命令
PS C:\Users\hhx> jstack 2412
Found one Java-level deadlock:
=============================
"t1":
waiting to lock monitor 0x00000217ffa2f400 (object 0x0000000719d5ffe0, a java.lang.Object),
which is held by "t2"
"t2":
waiting to lock monitor 0x00000217ffa2fc00 (object 0x0000000719d5ffd0, a java.lang.Object),
which is held by "t1"
活锁:两个线程都在不断地运行,但是由于互相改变结束条件,导致两个线程结束不了
static volatile int count = 10;
static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
while (count > 0) {
count--;
log.debug("count: {}", count);
}
}, "t1").start();
new Thread(() -> {
while (count < 20) {
count++;
log.debug("count: {}", count);
}
}, "t2").start();
}
饥饿,有的线程拿到锁的概率太低
public class Singleton{
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");
// 苏和阿要争抢c1(优先级低)
// 剩下3个,柏拉图c3需要亚,亚c4需要赫,赫的c5被占用概率小
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();
}
}
@Slf4j
class Philosopher extends Thread {
private Chopstick left;
private Chopstick right;
Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
private void eat() {
log.debug("eating...");
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
// 获得左筷子
synchronized (left) {
// 获得右筷子
synchronized (right) {
eat();
}// 放下右筷子
}// 放下左筷子
}
}
}
@ToString
@AllArgsConstructor
class Chopstick {
String name;
}
ReentrantLock
可中断(避免线程得不到锁一直等待)
可设置超时时间(使用trylock()方法,不会死等)
ReentrantLock lock = new ReentrantLock();
lock.lock();// 获得锁(不可打断,线程得不到锁会一直等待)(lockInterruptibly()可打断)
try{
// 临界区
} finally {
lock.unlock();
}
// ReentrantLock支持多间休息室
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();// 创建休息室(相当于waitSet)
new Thread(() -> {
lock.lock();// 获得锁
try{
// 临界区
try {
condition1.await(); // 进入休息室等待,await前需要先获得锁,await会释放锁
log.debug("醒了======>");
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
try{
condition1.signal(); // 唤醒condition1休息室中的线程,重新竞争lock锁
//condition1.signalAll();
}finally {
lock.unlock();
}
}).start();
}