線程活性問題是由資源稀缺或者程序自身設計缺陷導致線程一直處于非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 +
'}';
}
}