第七章 线程的活性故障--《java多线程编程实战指南-核心篇》

线程活性故障是由资源稀缺型或者程序自身的问题和缺陷导致线程一直处于非RUNNABLE状态,或者线程虽然处于RUNNABLE状态但是其要执行的任务却一直无法进展的故障现象。

死锁

如果两个或者更多的线程因相互等待对方而被永远暂停,那么我们就称这些线程产生了死锁。

哲学家就餐问题

一个线程在持有一个锁L1的情况下去申请另外一个锁L2,而另外一个线程也在持有一个锁L2的情况下去申请另外一个锁L1,那么就产生了死锁。

哲学家思考问题案例V1:

package JavaCoreThreadPatten.capter07.v1;

/**
 * 模拟多线程:哲学家吃饭问题
 * 筷子类
 */
public class Chopstick {
    public final int id;
    public Status status = Status.DOWN;

    public Chopstick(int id){
        this.id = id;
    }

    /**
     * 拿起筷子
     */
    public void pickUp(){
        this.status = Status.UP;
    }

    /**
     * 放下筷子
     */
    public void putDown(){
        this.status = Status.DOWN;
    }

    public int getId() {
        return id;
    }

    public enum Status{
        UP,DOWN;
    }
}
package JavaCoreThreadPatten.capter07.v1;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 哲学家:一会思考,一会吃饭,先拿起左手筷子再拿起右手筷子,最终再吃饭,
 */
public class Philosophers extends Thread {
    private final int id;
    private final Chopstick left;
    private final Chopstick right;

    public Philosophers(int id, Chopstick left, Chopstick right) {
        this.id = id;
        this.left = left;
        this.right = right;
    }


    public void eat() {
        synchronized (left) {
            left.pickUp();
            System.out.println("哲学家" + id + "拿起了左手边的筷子" + left.getId() + ",开始准备拿起右手边的筷子。。。" + right.getId());
            synchronized (right) {
                right.pickUp();
                System.out.println("哲学家" + id + "拿起了右手边的筷子" + right.getId() + ",开始吃饭。。。");
                doEat();
                //放下筷子,思考
                right.putDown();
            }
            left.putDown();
        }
    }

    /**
     * 吃饭动作,模拟随机停顿5秒钟
     */
    private void doEat() {
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException e) {
        }
    }

    /**
     * 哲学家思考
     */
    public void think() {
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException e) {
        }
    }

    @Override
    public void run() {
        for(;;){
            this.eat();
            this.think();
        }
    }
}
package JavaCoreThreadPatten.capter07.v1;

import java.util.ArrayList;
import java.util.List;

/**
 * 查看线程dump
 * Java stack information for the threads listed above:
 * ===================================================
 * "Thread-4":
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.eat(Philosophers.java:26)
 *         - waiting to lock <0x000000076f7de890> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         - locked <0x000000076f7de028> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.run(Philosophers.java:59)
 * "Thread-0":
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.eat(Philosophers.java:26)
 *         - waiting to lock <0x000000076f7e1fa0> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         - locked <0x000000076f7de890> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.run(Philosophers.java:59)
 * "Thread-1":
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.eat(Philosophers.java:26)
 *         - waiting to lock <0x000000076f7e2040> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         - locked <0x000000076f7e1fa0> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.run(Philosophers.java:59)
 * "Thread-2":
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.eat(Philosophers.java:26)
 *         - waiting to lock <0x000000076f7de010> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         - locked <0x000000076f7e2040> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.run(Philosophers.java:59)
 * "Thread-3":
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.eat(Philosophers.java:26)
 *         - waiting to lock <0x000000076f7de028> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         - locked <0x000000076f7de010> (a JavaCoreThreadPatten.capter07.v1.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v1.Philosophers.run(Philosophers.java:59)
 *
 *         可以看出来其实已经形成了一个环形锁:产生了死锁
 */
public class Test {
    public static void main(String[] args) {
        //五个哲学家,五支筷子
        List<Chopstick> chopsticks = new ArrayList<>();
        for(int i=0;i<5;i++){
            chopsticks.add(new Chopstick(i));
        }
        List<Philosophers> philosophers = new ArrayList<>();
        for(int i=0;i<5;i++){
            if(i<4){
                philosophers.add(new Philosophers(i,chopsticks.get(i),chopsticks.get(i+1)));
            }else{
                philosophers.add(new Philosophers(i,chopsticks.get(i),chopsticks.get(0)));
            }
        }
        philosophers.forEach(p->{
            p.start();
        });
    }
}

哲学家问题案例V2(jdk内部锁):

package JavaCoreThreadPatten.capter07.v2;

/*
    筷子实体
 */
public class Chopstick {
    private final int id;
    private Status status = Status.PUT_DOWN;

    public Chopstick(int id) {
        this.id = id;
    }

    public void pickUp(){
        this.status = Status.PICKED_UP;
    }

    public void putDown(){
        this.status = Status.PUT_DOWN;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "id=" + id +
                '}';
    }

    private enum Status{
        PUT_DOWN,PICKED_UP;
    }
}
package JavaCoreThreadPatten.capter07.v2;

/**
 * 执行方法抽象函数
 */
public abstract class AbstractTest {
    protected final int initNum;
    protected final Chopstick[] chopsticks;
    protected final AbstractPhilosopher[] abstractPhilosophers;

    public AbstractTest(int initNum) {
        this.initNum = initNum;
        this.chopsticks = initChopstick();
        this.abstractPhilosophers = initPhilosopher();
    }

    /**
     * 初始化筷子
     * @return
     */
    protected Chopstick[] initChopstick(){
        Chopstick[] chopsticks = new Chopstick[initNum];
        for(int i=0;i<initNum;i++){
            chopsticks[i] = new Chopstick(i);
        }
        return chopsticks;
    }

    /**
     * 初始化哲学家,由子类进行实现
     * @return
     */
    protected abstract AbstractPhilosopher[] initPhilosopher();

    /**
     * 运行方法
     */
    protected final void run(){
        if(abstractPhilosophers==null || abstractPhilosophers.length<=0){
            System.err.println("初始化未完成");
        }
        for(AbstractPhilosopher abstractPhilosopher:abstractPhilosophers){
            abstractPhilosopher.start();
        }
    }
}

 

package JavaCoreThreadPatten.capter07.v2;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 哲学家抽象类
 */
public abstract class AbstractPhilosopher extends Thread {

    protected final int id;
    protected final Chopstick left;
    protected final Chopstick right;

    public AbstractPhilosopher(int id, Chopstick left, Chopstick right) {
        this.id = id;
        this.left = left;
        this.right = right;
    }

    public abstract void eat();

    public void think(){
        try {
            System.out.println("我是哲学家:"+id+",我开始思考。。。");
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        for(;;){
            eat();
            think();
        }
    }

    public void doEat(){
        try {
            System.out.println("我是哲学家:"+id+",我开始吃饭。。。");
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package JavaCoreThreadPatten.capter07.v2;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 导致死锁的哲学家模型
 */
public class DeadlockingPhilosopher extends AbstractPhilosopher{

    public DeadlockingPhilosopher(int id, Chopstick left, Chopstick right) {
        super(id, left, right);
    }

    @Override
    public void eat() {
        synchronized (super.left){
            System.out.println(id+"拿起左手边的筷子:"+left);
            super.left.pickUp();
            synchronized (super.right){
                System.out.println(id+"拿起右手边的筷子:"+right+";开始吃饭啦。。");
                super.right.pickUp();
                try {
                    TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                } catch (InterruptedException e) {
                }
                super.right.putDown();
            }
            super.left.putDown();
        }
    }
}
package JavaCoreThreadPatten.capter07.v2;

import java.util.List;

/**
 * 哲学家死锁问题模拟程序
 * Java stack information for the threads listed above:
 * ===================================================
 * "Thread-1":
 *         at JavaCoreThreadPatten.capter07.v2.DeadlockingPhilosopher.eat(DeadlockingPhilosopher.java:21)
 *         - waiting to lock <0x000000076bb24918> (a JavaCoreThreadPatten.capter07.v2.Chopstick)
 *         - locked <0x000000076bb28978> (a JavaCoreThreadPatten.capter07.v2.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v2.AbstractPhilosopher.run(AbstractPhilosopher.java:35)
 * "Thread-0":
 *         at JavaCoreThreadPatten.capter07.v2.DeadlockingPhilosopher.eat(DeadlockingPhilosopher.java:21)
 *         - waiting to lock <0x000000076bb28978> (a JavaCoreThreadPatten.capter07.v2.Chopstick)
 *         - locked <0x000000076bb24918> (a JavaCoreThreadPatten.capter07.v2.Chopstick)
 *         at JavaCoreThreadPatten.capter07.v2.AbstractPhilosopher.run(AbstractPhilosopher.java:35)
 *
 * Found 1 deadlock.
 *
 * 我们可以发现一个死锁问题
 */
public class DiningPhilosopherTest extends AbstractTest{
    public DiningPhilosopherTest(int initNum) {
        super(initNum);
    }

    public static void main(String[] args) {
        AbstractTest abstractTest = new DiningPhilosopherTest(2);
        abstractTest.run();
    }

    @Override
    protected AbstractPhilosopher[] initPhilosopher() {
        DeadlockingPhilosopher[] deadlockingPhilosophers = new DeadlockingPhilosopher[initNum];
        for(int i=0;i<initNum;i++){
            deadlockingPhilosophers[i] = new DeadlockingPhilosopher(i,chopsticks[i],chopsticks[(i+1)%initNum]);
        }
        return deadlockingPhilosophers;
    }
}

哲学家问题V3(基于显示锁):

package JavaCoreThreadPatten.capter07.v2;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 显示锁导致可能出现死锁问题的哲学家
 */
public class BuggyLckBasedPhilosopher extends AbstractPhilosopher{
    /**
     * 确保每个筷子有一个显示锁与之对应,则确保该类的每一个实例共享同一个锁map
     */
    private static final ConcurrentHashMap<Chopstick, Lock> LOCK_MAP;
    static {
        LOCK_MAP = new ConcurrentHashMap<>();
    }

    public BuggyLckBasedPhilosopher(int id, Chopstick left, Chopstick right) {
        super(id, left, right);
        LOCK_MAP.putIfAbsent(left,new ReentrantLock());
        LOCK_MAP.putIfAbsent(right,new ReentrantLock());
    }

    @Override
    public void eat() {
        if(pickUpChopstick(left) && pickUpChopstick(right)){
            doEat();
        }
        putDownChopstick(right);
        putDownChopstick(left);
        think();
    }

    protected boolean pickUpChopstick(Chopstick chopstick){
        final Lock lock = LOCK_MAP.get(chopstick);
        try{
            lock.lock();
            System.out.println("哲学家"+id+"捡起筷子"+chopstick);
            chopstick.pickUp();
            return true;
        }catch (Exception e){
            lock.unlock();
        }
        return false;
    }

    protected void putDownChopstick(Chopstick chopstick){
        final Lock lock = LOCK_MAP.get(chopstick);
        try{
            System.out.println("哲学家"+id+"放下筷子"+chopstick);
            chopstick.putDown();
            lock.unlock();
        }catch (Exception e){
            lock.unlock();
        }
    }

    @Override
    public void run() {
        for(;;){
            eat();
        }
    }
}
package JavaCoreThreadPatten.capter07.v2;

/**
 * 显示锁
 * Found one Java-level deadlock:
 * =============================
 * "Thread-1":
 *   waiting for ownable synchronizer 0x000000076bb34200, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
 *   which is held by "Thread-0"
 * "Thread-0":
 *   waiting for ownable synchronizer 0x000000076bb342a0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
 *   which is held by "Thread-1"
 *
 * Java stack information for the threads listed above:
 * ===================================================
 * "Thread-1":
 *         at sun.misc.Unsafe.park(Native Method)
 *         - parking to wait for  <0x000000076bb34200> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 *         at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
 *         at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
 *         at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.pickUpChopstick(BuggyLckBasedPhilosopher.java:38)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.eat(BuggyLckBasedPhilosopher.java:27)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.run(BuggyLckBasedPhilosopher.java:62)
 * "Thread-0":
 *         at sun.misc.Unsafe.park(Native Method)
 *         - parking to wait for  <0x000000076bb342a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
 *         at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
 *         at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
 *         at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
 *         at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.pickUpChopstick(BuggyLckBasedPhilosopher.java:38)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.eat(BuggyLckBasedPhilosopher.java:27)
 *         at JavaCoreThreadPatten.capter07.v2.BuggyLckBasedPhilosopher.run(BuggyLckBasedPhilosopher.java:62)
 *
 * Found 1 deadlock.
 * 出现死锁
 */
public class BuggyLckBasedTest extends AbstractTest{

    public BuggyLckBasedTest(int initNum) {
        super(initNum);
    }

    public static void main(String[] args) {
        AbstractTest abstractTest = new BuggyLckBasedTest(2);
        abstractTest.run();
    }

    @Override
    protected AbstractPhilosopher[] initPhilosopher() {
        BuggyLckBasedPhilosopher[] buggyLckBasedPhilosophers = new BuggyLckBasedPhilosopher[initNum];
        for(int i=0;i<initNum;i++){
            buggyLckBasedPhilosophers[i] = new BuggyLckBasedPhilosopher(i,chopsticks[i],chopsticks[(i+1)%initNum]);
        }
        return buggyLckBasedPhilosophers;
    }
}

死锁产生的必要条件:

  • 资源互斥。涉及的资源必须是独占的,即每个资源一次只能够被一个线程使用。
  • 资源不可抢夺。涉及的资源只能够被其持有者主动释放,而无法被资源的持有者和申请者之外的第三方线程所抢夺。
  • 占用并等待资源。涉及的线程当前至少持有一个资源(资源A)并申请其他资源(资源B),而这些资源(资源B)恰好被其他线程持有。在这个资源等待的过程中,线程并不释放其已经持有的资源。
  • 循环等待资源。涉及的线程必须在等待别的线程持有的资源,而这些线程又反过来在等待第一个线程所持有的资源。即获取资源的顺序是反向的。

产生死锁的代码特征就是在持有一个锁的情况下去申请另外一个锁,这通常意味着锁的嵌套。

一个线程在已经持有一个锁的情况下再次申请这个锁并不会导致死锁,这是因为java中的锁都是可重入的,这种情况下线程再次申请这个锁是可以成功的。

一般解决死锁从消除“占用并等待资源”和消除“循环等待资源”两个方向入手。

解决死锁的方式:

1.锁粗化:使用粗粒度的锁代替多个锁,这样涉及的线程都只需要申请一个锁从而避免了死锁,案例改造如下:

package JavaCoreThreadPatten.capter07.v2;

import java.util.Objects;

/**
 * 通过粗粒度的锁规避死锁
 */
public class GlobalLckBasedPhilosopher extends AbstractPhilosopher{
    private static final Object LOCK = new Object();

    public GlobalLckBasedPhilosopher(int id, Chopstick left, Chopstick right) {
        super(id, left, right);
    }

    @Override
    public void eat() {
        synchronized (LOCK){
            System.out.println("获取到全局锁");
            left.pickUp();
            right.pickUp();
            doEat();
            right.putDown();
            left.putDown();
            System.out.println("哲学家"+id+"吃饭完毕");
        }
        think();
    }
}
package JavaCoreThreadPatten.capter07.v2;

/**
 * 粗粒度锁解决死锁问题:实际上大大降低了并发性,转变成了串行
 */
public class GlobalLockBasedTest extends AbstractTest{
    public GlobalLockBasedTest(int initNum) {
        super(initNum);
    }

    public static void main(String[] args) {
        AbstractTest abstractTest = new GlobalLockBasedTest(2);
        abstractTest.run();
    }

    @Override
    protected AbstractPhilosopher[] initPhilosopher() {
        GlobalLckBasedPhilosopher[] globalLckBasedPhilosophers = new GlobalLckBasedPhilosopher[initNum];
        for(int i=0;i<initNum;i++){
            globalLckBasedPhilosophers[i] = new GlobalLckBasedPhilosopher(i,chopsticks[i],chopsticks[(i+1)%initNum]);
        }
        return globalLckBasedPhilosophers;
    }
}

锁粗话明显地降低了并发性,导致资源的浪费。

2.锁排序法:相关线程使用全局统一的顺序申请锁,确定获取锁的顺序,消除循环等待资源的问题,避免死锁。一般使用对象的hashcode(System.identityHashCode(Object))来作为资源的排序依据。具体案例如下:

package JavaCoreThreadPatten.capter07.v2;

/**
 * 使用锁排序规避死锁:总是从小到大获取锁
 */
public class FixedPhilosopher extends AbstractPhilosopher{
    private Chopstick one;
    private Chopstick other;
    public FixedPhilosopher(int id, Chopstick left, Chopstick right) {
        super(id,left,right);
        if(System.identityHashCode(left) > System.identityHashCode(right)){
            Chopstick mid = left;
            one = right;
            other = mid;
        }else{
            one = left;
            other = right;
        }
    }

    @Override
    public void eat() {
        synchronized (one){
            System.out.println(id+"拿起第一根筷子:"+one);
            one.pickUp();
            synchronized (other){
                System.out.println(id+"拿起另外一根筷子:"+other+";开始吃饭啦。。");
                other.pickUp();
                doEat();
                other.putDown();
            }
            one.putDown();
        }
        think();
    }
}
package JavaCoreThreadPatten.capter07.v2;

/**
 * 通过按照锁的hashCode顺序来获取锁,解决死锁问题,因为都是从小到大获取锁,
 * 按照统一的顺序申请锁,消除循环等待资源问题
 */
public class FixedPhiosopherTest extends AbstractTest{
    public FixedPhiosopherTest(int initNum) {
        super(initNum);
    }

    public static void main(String[] args) {
        AbstractTest abstractTest = new FixedPhiosopherTest(2);
        abstractTest.run();
    }

    @Override
    protected AbstractPhilosopher[] initPhilosopher() {
        FixedPhilosopher[] fixedPhilosophers = new FixedPhilosopher[initNum];
        for(int i=0;i<initNum;i++){
            fixedPhilosophers[i] = new FixedPhilosopher(i,chopsticks[i],chopsticks[(i+1)%initNum]);
        }
        return fixedPhilosophers;
    }
}

3.使用ReetrantLock.tryLock(long,TimeUnit)申请锁,避免一个线程无限制地等待另外一个线程持有的资源,从而消除“占用并等待资源”这个条件,案例如下:

package JavaCoreThreadPatten.capter07.v2;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class FixedLockBasedPhiosopher extends AbstractPhilosopher{

    private static final ConcurrentHashMap<Chopstick, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<>();

    public FixedLockBasedPhiosopher(int id, Chopstick left, Chopstick right) {
        super(id, left, right);
        LOCK_MAP.putIfAbsent(left,new ReentrantLock());
        LOCK_MAP.putIfAbsent(right,new ReentrantLock());
    }

    @Override
    public void eat() {
        ReentrantLock leftLock = LOCK_MAP.get(left);
        ReentrantLock rightLock = LOCK_MAP.get(right);
        try {
            boolean b = leftLock.tryLock(3, TimeUnit.SECONDS);
            if(b){
                System.out.println("哲学家"+id+"获取到左边的锁,嘚瑟一下");
                left.pickUp();
                b = rightLock.tryLock(3,TimeUnit.SECONDS);
                if(b){
                    right.pickUp();
                    System.out.println("哲学家"+id+"获取到又边的锁,吃饭。。。");
                    doEat();
                    rightLock.unlock();
                }
                leftLock.unlock();
            }
        } catch (InterruptedException e) {
            if(leftLock.isLocked()){
                leftLock.unlock();
            }
            if(rightLock.isLocked()){
                rightLock.unlock();
            }
        }
    }

    @Override
    public void run() {
        for(;;){
            eat();
            think();
        }
    }
}
package JavaCoreThreadPatten.capter07.v2;

/**
 * 使用ReentrantLock.tryLock(timeout,timeUnit)来解决死锁问题
 */
public class FixedLockBasedTest extends AbstractTest{
    public FixedLockBasedTest(int initNum) {
        super(initNum);
    }

    public static void main(String[] args) {
        AbstractTest abstractTest = new FixedLockBasedTest(2);
        abstractTest.run();
    }

    @Override
    protected AbstractPhilosopher[] initPhilosopher() {
        FixedLockBasedPhiosopher[] fixedLockBasedPhiosophers = new FixedLockBasedPhiosopher[initNum];
        for(int i=0;i<initNum;i++){
            fixedLockBasedPhiosophers[i] = new FixedLockBasedPhiosopher(i,chopsticks[i],chopsticks[(i+1)%initNum]);
        }
        return fixedLockBasedPhiosophers;
    }
}

4.外部调用导致的死锁。还有一种常见的情况是一个方法在持有一个锁的情况下调用一个外部方法。假设类A有两个同步方法syncA和syncB,类B有两个同步方法syncC和syncD,syncA会调用syncC,syncD会调用syncB。这里,syncA和syncD这两个方法虽然不直接具备死锁特征,但是由于他们调用了一个外部方法,而这个方法是一个同步方法,因此这两个方法实际上具备了死锁特征。当一个线程在执行A.syncA时,另外一个线程正在执行B.syncD,那么这两个线程就有可能产生死锁。一般地,一个方法在持有一个锁的情况下调用一个外部方法,而外部方法往往不在我们的控制范围之内,其自身可能不会申请另外一个锁,也可能会申请另外一个锁,因此一般针对这种情况我们使用开放调用来规避,就是在一个方法在调用外部方法的时候不持有任何锁。

规避死锁的常见方式:

  • 粗锁发--使用一个粗粒度的锁代替多个锁
  • 锁排序法--相关线程使用全局统一的顺序申请锁
  • 使用ReentranLock.tryLock(long,TimeUnit)来申请锁
  • 使用开放调用--在调用外部方法时不加锁
  • 使用锁的替代品(无状态对象、线程持有对象、volatile关键字等),这些能避免锁的开销

死锁的恢复

由于导致死锁的线程的不可控性,因此死锁恢复的实际可操作性并不强:对死锁进行的故障恢复尝试可能是徒劳的(故障线程可无法响应中断)且有害的(可能导致活锁等问题)

死锁的自动恢复有赖于线程的中断机制,其基本思想是:定义一个工作者线程专门用于死锁检测与恢复。该线程定期检测系统中是否存在死锁,若检测到死锁,则随机选取一个死锁线程并给其发送中断。该中断使得一个任意的死锁线程被java虚拟机唤醒,从而使其抛出InterruptedException异常。这使得目标线程不再等待它本身永远无法申请到的资源,从而破坏了死锁产生的必要条件中的“占用并等待资源”中的等待资源部分。目标线程则通过对InterruptedException进行处理的方式来响应中断:目标线程捕获InterruptedException异常后将其已经持有的资源主动释放掉,这相当于破坏了死锁产生的必要条件中的“占用并等待资源”中的占用资源部分。接着线程继续监测系统是否仍然存在死锁,若存在,则继续选中一个任意的死锁线程并给其发送中断,直到系统中不再存在死锁。

解决死锁是通过java.lang.management.ThreadMXBean.findDeadlockedThreads()调用来实现死锁检测的。ThreadMXBean.findDeadlockedThreads()能够返回一组死锁线程的线程编号。ThreadMXBean类是JMX API的一部分,因此其提供的功能也可以通过jconsole、jvisualvm手工调用。

线程饥饿是指线程一直无法获得其所需的资源而导致其任务一直无法进展的一种活性故障。

线程饥饿的一个典型例子是在高争用的环境性使用非公平模式的读写锁。

活锁是指线程一直处于运行状态,但是其任务却一直无法进展的一种活性故障。

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章