线程活性问题是由资源稀缺或者程序自身设计缺陷导致线程一直处于非RUNNABLE状态,或者线程虽然处于RUNNABLE状态,但是要执行的任务一直无法进展。
如果两个或者更多的线程因相互等待而被永远暂停,我们就称这些线程产生了死锁。
有关死锁的一个经典问题是哲学家就餐问题。
package com.wkcto.threadactivity.deadlock;
/**
* 定义筷子类
*/
public class Chopstick {
public final int id; //筷子编号
public Chopstick(int id) {
this.id = id;
}
@Override
public String toString() {
return "Chopstick-" + id;
}
}
package com.wkcto.threadactivity.deadlock;
/**
* 模拟哲学家就餐
*/
public class Test {
public static void main(String[] args) {
int num = 5; //有5个哲学家,5根筷子
//创建一个存储5根筷子的数组
Chopstick[] chopsticks = new Chopstick[num];
//给数组中的筷子赋值
for (int i = 0; i < chopsticks.length; i++) {
chopsticks[i] = new Chopstick(i);
}
//创建5个哲学家线程
for (int i = 0; i < num; i++) {
Philosopher philosopher = new Philosopher(i, chopsticks[i], chopsticks[ (i+1) % num]);
philosopher.start();
}
}
}
package com.wkcto.threadactivity.deadlock;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 定义哲学家类,继承Thread线程类
* 使用显示锁ReentrantLock的tryLock()方法申请锁, 允许在申请锁时指定一个超时时间
*/
public class Philosopher extends Thread {
public final int id; //哲学家编号
public final Chopstick left; //左边筷子
public final Chopstick right; //右边筷子
public Philosopher(int id, Chopstick left, Chopstick right) {
super("Philosopher==" + id);
this.id = id;
this.left = left;
this.right = right;
}
//哲学家不断的思考与吃饭
@Override
public void run() {
while (true) {
think();
eat();
}
}
final ReentrantLock leftLock = new ReentrantLock();
final ReentrantLock rightLock = new ReentrantLock();
private void eat() {
//吃饭需要先拿左边筷子,这根筷子就是被当前哲学家独占使用
try {
if (leftLock.tryLock(100, TimeUnit.MILLISECONDS)){
System.out.println(this + " 拿起左边筷子");
Thread.sleep(new Random().nextInt(100));
if (rightLock.tryLock(100, TimeUnit.MILLISECONDS)){
System.out.println(this + " 拿起右边筷子, 有了一双筷子,可以吃饭了");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (rightLock.isHeldByCurrentThread()){
rightLock.unlock();
}
if (leftLock.isHeldByCurrentThread()){
leftLock.unlock();
}
}
}
private void think() {
System.out.println(this + " 哲学家正在思考.....");
try {
Thread.sleep(new Random().nextInt(100)); //模拟思考时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Philosopher{" +
"id=" + id +
", left=" + left +
", right=" + right +
'}';
}
}
package com.wkcto.threadactivity.deadlock;
import java.util.Random;
/**
* 定义哲学家类,继承Thread线程类
*/
public class Philosopher1 extends Thread{
public final int id; //哲学家编号
public final Chopstick left; //左边筷子
public final Chopstick right; //右边筷子
public Philosopher1(int id, Chopstick left, Chopstick right) {
super("Philosopher==" + id );
this.id = id;
this.left = left;
this.right = right;
}
//哲学家不断的思考与吃饭
@Override
public void run() {
while (true){
think();
eat();
}
}
private void eat() {
//吃饭需要先拿左边筷子,这根筷子就是被当前哲学家独占使用
synchronized ( left ){
System.out.println(this + " 拿起左边的筷子");
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
//有了左边筷子后,还需要拿右边筷子
synchronized (right){
System.out.println(this + "有了一双筷子,可以吃饭");
}
}
}
private void think() {
System.out.println( this + " 哲学家正在思考.....");
try {
Thread.sleep(new Random().nextInt(100)); //模拟思考时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Philosopher{" +
"id=" + id +
", left=" + left +
", right=" + right +
'}';
}
}
package com.wkcto.threadactivity.deadlock;
import java.util.Random;
/**
* 定义哲学家类,继承Thread线程类
* 可以使用粗锁化解决死锁问题
* 某一时刻只允许一个哲学家吃饭,在某个哲学家吃饭时,其他哲学家要么进行思考,要么等待
* 实际上,哲学家吃饭只需要两根筷子, 现在有5根筷子,可以允许两个哲学家同时吃饭. 现在使用粗锁化只允许有一个哲学家吃饭,出现了资源浪费的情况
*/
public class Philosopher2 extends Thread{
public final int id; //哲学家编号
public final Chopstick left; //左边筷子
public final Chopstick right; //右边筷子
public Philosopher2(int id, Chopstick left, Chopstick right) {
super("Philosopher==" + id );
this.id = id;
this.left = left;
this.right = right;
}
//哲学家不断的思考与吃饭
@Override
public void run() {
while (true){
think();
eat();
}
}
private static final Object OBJ = new Object(); //定义常量作为锁对象
private void eat() {
synchronized ( OBJ ){
System.out.println(this + " 拿起左边的筷子");
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this + "拿起右边筷子后,就有了一双筷子,可以吃饭");
}
}
private void think() {
System.out.println( this + " 哲学家正在思考.....");
try {
Thread.sleep(new Random().nextInt(100)); //模拟思考时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Philosopher{" +
"id=" + id +
", left=" + left +
", right=" + right +
'}';
}
}
package com.wkcto.threadactivity.deadlock;
import java.util.Random;
/**
* 定义哲学家类,继承Thread线程类
* 可以保证所有线程使用相同的锁的顺序来避免死锁
* 可以给所有的筷子设置一个编号, 对于哲学家来说 ,始终先拿编号小的筷子,再拿编号大的筷子
*/
public class Philosopher3 extends Thread{
public final int id; //哲学家编号
public final Chopstick left; //左边筷子
public final Chopstick right; //右边筷子
public Philosopher3(int id, Chopstick left, Chopstick right) {
super("Philosopher==" + id );
this.id = id;
//根据筷子编号赋值,如果left筷子编号小于right编号正常赋值
if (left.id < right.id){
this.left = left;
this.right = right;
}else {
this.left = right;
this.right = left;
}
}
//哲学家不断的思考与吃饭
@Override
public void run() {
while (true){
think();
eat();
}
}
private void eat() {
//吃饭需要先拿左边筷子,这根筷子就是被当前哲学家独占使用
synchronized ( left ){
System.out.println(this + " 拿起左边的筷子");
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
//有了左边筷子后,还需要拿右边筷子
synchronized (right){
System.out.println(this + "有了一双筷子,可以吃饭");
}
}
}
private void think() {
System.out.println( this + " 哲学家正在思考.....");
try {
Thread.sleep(new Random().nextInt(100)); //模拟思考时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Philosopher{" +
"id=" + id +
", left=" + left +
", right=" + right +
'}';
}
}