第七章 线程的活性故障--《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) {}}@Overridepublic 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;}@Overridepublic 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();}}@Overridepublic 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);}@Overridepublic 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();}@Overrideprotected 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());}@Overridepublic 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();}}@Overridepublic 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();}@Overrideprotected 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);}@Overridepublic 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();}@Overrideprotected 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;}}@Overridepublic 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();}@Overrideprotected 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());}@Overridepublic 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();}}}@Overridepublic 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();}@Overrideprotected 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手工调用。
线程饥饿是指线程一直无法获得其所需的资源而导致其任务一直无法进展的一种活性故障。
线程饥饿的一个典型例子是在高争用的环境性使用非公平模式的读写锁。
活锁是指线程一直处于运行状态,但是其任务却一直无法进展的一种活性故障。
第七章 线程的活性故障--《java多线程编程实战指南-核心篇》相关推荐
- 《java多线程编程实战指南 核心篇》读书笔记二
1. 竞态 对于同样的输入,程序的输出有时候正确而有时候却是错误的.这种一个计算结果的正确性与时间有关的现象就被称为竞态(RaceCondition) 导致竞态的常见原因是多个线程在没有采取任何措施的 ...
- 《java多线程编程实战指南 核心篇》读书笔记一
1. run方法是线程的任务处理逻辑的入口方法,它由java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用. 2. 启动一个线程的实质是请求java虚拟机运行相应的线程,而这个线程具体何时能 ...
- 《java多线程编程实战指南 核心篇》读书笔记三
1. 锁的思路: 将多个线程对共享数据的并发访问转换为串行访问,即一个共享数据一次只能被一个线程访问,该线程访问结束后其他线程才能对其进行访问.锁(lock)就是利用这种思路以保障线程安全的线程同步机 ...
- Java多线程编程实战指南+设计模式篇pdf
下载地址:网盘下载 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而 解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然 ...
- Java多线程编程实战指南
内容简介 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然而,多线程编 ...
- java多线程编程_Java多线程编程实战指南+设计模式篇.pdf
Java多线程编程实战指南+设计模式篇.pdf 对Java架构技术感兴趣的工程师朋友们可以关注我,转发此文后私信我"Java"获取更多Java编程PDF资料(附送视频精讲) 关注我 ...
- java多线程编程_《java多线程编程实战指南》读书笔记 -- 基本概念
展开 并发:多个线程操作相同资源,保证线程安全,合理使用资源 高并发:服务能同时处理多个请求,提高程序性能 测试上下文切换工具 Lmbench3 测量上下文切换时长 vmstat 测量上下文切换次数 ...
- java多线程实战指南_学习笔记《Java多线程编程实战指南》二
2.1线程属性 属性 属性类型及用途 只读属性 注意事项 编号(id) long型,标识不同线程 是 不适合用作唯一标识 名称(name) String型,区分不同线程 否 设置名称有助于代码调试和问 ...
- 你花了多久弄明白架构设计?java多线程编程实战指南pdf
一面 自我介绍 项目中的监控:那个监控指标常见的有哪些? 微服务涉及到的技术以及需要注意的问题有哪些? 注册中心你了解了哪些? consul 的可靠性你了解吗? consul 的机制你有没有具体深入过 ...
最新文章
- MPLS 第一话 :传统IP路由的局限性
- (0019)iOS 开发之关于__weak修饰NSString以及内存管理的问题
- Quartz教程:快速入门
- Meta-Learning
- dz php表单发送邮件,php 发送邮件
- 【花式】基于matlab花圣诞树【含Matlab源码 001期】
- 曲折中前行的汉语编程
- 计算机学术论文写作与发表
- syncnavigator关于win10、win8系统无法注册机进行激活的问题
- python报错Statements must be separated by newlines or semicolons解决方法
- 童年记忆中做过的农活
- HTML中enter键触发事件
- 神鬼世界更新完了为什么显示与服务器断开连接,全新服务器构架 神鬼世界6月23日数据互通公告...
- Ubuntu Server 12.04 搭建 hadoop 集群版环境——基于VirtualBox
- pixel一代升级android10,谷歌Pixel系列首次Android 10更新:我们都应该知道些什么
- 请问汽车CD接线各个的字母代表什么,ACC,ILL,RR,FR,FL,RLANT,B/U,NC,CND,真诚的谢谢了!
- 用一个原始密码针对不同软件生成不同密码并保证相对安全
- 【37】警告gyroscope bias initial calibration -nan -nan -nan
- MySql的安装和卸载
- golang学习笔记8 beego参数配置 打包linux命令
热门文章
- zsh: command not find解决办法
- Python输出三角形图案
- LeetCode 387、字符串中的第一个唯一字符
- 蒲公英内侧:项目打包成ipa文件,创建证书和描述文件流程
- Localization of Classified Objects in SLAM using NonparametricStatistics and Clustering(2018,IROS)
- 细说网络负载均衡技术
- html div flex,CSS中flex布局详解
- 【技术向】VOT中的EAO是如何计算的
- java实现图片上传至本地
- vue使用element-ui table 清除表格背景色以及表格边框线