1.死锁:通俗地说,死锁是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象,死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重的影响
用来描述死锁的一个有名的场景是“哲学家就餐”问题,用一个简单的例子来模拟下,有两个科学家和两把叉子
package LockTest;
public class DeadLock extends Thread{
protected Object tool;
static Object fork1=new Object();
static Object fork2=new Object();
public DeadLock(Object obj){
this.tool=obj;
if(tool==fork1){
this.setName("哲学家A");
}
else if(tool==fork2){
this.setName("哲学家B");
}
}
public void run(){
if(tool==fork1){
synchronized(fork1){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(fork2){
System.out.println("哲学家A开始吃饭了");
}
}
}
if(tool==fork2){
synchronized(fork2){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(fork1){
System.out.println("哲学家B开始吃饭了");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
DeadLock PhilosopherA=new DeadLock(fork1);
DeadLock PhilosopherB=new DeadLock(fork2);
PhilosopherA.start();
PhilosopherB.start();
Thread.sleep(1000);
}
}
哲学家A先占用叉子1,哲学家B占用叉子2,接着他们就相互等待,都没有办法同时获得两个叉子用餐。实际环境中,遇到了这种情况,通常的表现就是相关的进程不再工作,并且CPU占用率为0(因为死锁的线程不占用CPU),可以使用JDK确认问题:使用jps命令得到java进程的进程ID,接着使用jstack命令得到线程的线程堆栈:
找到了死锁的存在,并在最后可以看出两者互相等待的锁的ID
2.饥饿
某一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行,比如它的线程优先级可能太低,而高优先级的线程不断抢占它需要的资源,导致低优先级线程无法工作。与死锁相比,饥饿还是有可能在未来一段时间内解决的,比如高优先级的线程已经完成任务,不再疯狂地执行
3.活锁
任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试、失败、尝试、失败。活锁和死锁的区别在于,处于活锁的实体时在不断改变状态,活锁有可能自行解开而死锁不能