哲学家就餐问题
活跃性
死锁
一个线程需要同时获得多把锁,这时就容易发生死锁。
发生死锁的必要条件
互斥条件:在一段时间内,一种资源只能被一个进程所使用。
请求和保持条件:进程已经拥有了至少一种资源,同时又去申请其他资源。因为其他资源被别的进程所使用,该进程进入阻塞状态,并且不释放自己已有的资源。
不可剥夺条件:进程对已获得的资源在未使用完成前不能被强占,只能在进程使用完后自己释放。
循环等待条件:发生死锁时,必然存在一个进程——资源的循环链。
活锁
活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Slf4j(topic = "LiveLock") public class LiveLockDemo {
static volatile int count = 10; static final Object lock = new Object();
public static void main(String[] args) { new Thread(() -> { while (count > 0) { try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count--; log.debug("count: {}", count); } }, "t1").start(); new Thread(() -> { while (count < 20) { try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count++; log.debug("count: {}", count); } }, "t2").start(); } }
|
解决:在线程执行时,中途给予不同的间隔时间,让某个线程先结束即可。
死锁与活锁的区别:
饥饿
某些线程因为优先级太低,始终得不到CPU调度执行,也不能够结束。
在使用顺序加锁时,可能会出现饥饿现象:
顺序加锁的解决方案:
检测死锁方式
死锁代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package Thread.Day03;
import java.util.concurrent.TimeUnit;
public class Demo05 { public static void main(String[] args) { Object lockA = new Object(); Object lockB = new Object();
new Thread(()->{ synchronized (lockA){ System.out.println("等待B锁"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){
} } }).start();
new Thread(()->{ synchronized (lockB){ System.out.println("等待A锁"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockA){
} } }).start(); } }
|
检测
方式一
运行死锁代码
打开控制台使用jps查看java进程
使用Jstack命令
提示信息
方式二
1.Jconsole命令
连接进程
结果
死锁举例:哲学家就餐问题
有五位哲学家,围坐在圆桌旁:
使用synchronized
使用synchronized会出现死锁问题
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package Thread.Day03;
import java.util.concurrent.TimeUnit;
public class PhilosopherEating { public static void main(String[] args) { ChopStacks[] c = new ChopStacks[5];
for (int i = 0; i < c.length; i++) { c[i] = new ChopStacks(String.valueOf(i)); }
new Philosopher("苏格拉底", c[0], c[1]).start(); new Philosopher("柏拉图", c[1], c[2]).start(); new Philosopher("亚里士多德", c[2], c[3]).start(); new Philosopher("郝拉克利特", c[3], c[4]).start(); new Philosopher("阿基米德", c[4], c[0]).start(); } }
class Philosopher extends Thread { ChopStacks left; ChopStacks right;
public Philosopher(String name, ChopStacks left, ChopStacks right) { super(name); this.left = left; this.right = right; }
public void run() { while (true) { synchronized (left) { synchronized (right) { eat(); } } } }
private void eat() { System.out.println(Thread.currentThread().getName() + "eating................"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }
}
class ChopStacks { private String name;
public ChopStacks(String name) { this.name = name; } }
|
测试
当每一位哲学家都拿起一只筷子,就会互相等待,造成死锁现象
使用ReentrantLock
使用ReentrantLock的tryLock()方法进行尝试获取锁,当超时获取不到锁,就会主动放弃此次获取锁的行为
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package Thread.Day03;
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockPhilosopherEating { public static void main(String[] args) { ChopStacks01[] c = new ChopStacks01[5];
for (int i = 0; i < c.length; i++) { c[i] = new ChopStacks01(String.valueOf(i)); }
new Philosopher01("苏格拉底", c[0], c[1]).start(); new Philosopher01("柏拉图", c[1], c[2]).start(); new Philosopher01("亚里士多德", c[2], c[3]).start(); new Philosopher01("郝拉克利特", c[3], c[4]).start(); new Philosopher01("阿基米德", c[4], c[0]).start(); } }
class Philosopher01 extends Thread { ChopStacks01 left; ChopStacks01 right;
public Philosopher01(String name, ChopStacks01 left, ChopStacks01 right) { super(name); this.left = left; this.right = right; }
public void run() { while (true) { if (left.tryLock()) { try { if (right.tryLock()) { try { eat(); } finally { right.unlock(); } } } finally { left.unlock(); } } } }
private void eat() { System.out.println(Thread.currentThread().getName() + "eating................"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }
}
class ChopStacks01 extends ReentrantLock { private String name;
public ChopStacks01(String name) { this.name = name; } }
|
测试
不会出现死锁和饥饿现象