死锁的定义:
按书上所说,死锁就是自己拥有其他人需要的资源,同时又在等待其他人已经拥有的资源,并且每个人在获得所需要的资源之前都不会放弃自己已经有的资源。
死锁的类型:
1.简单的顺序死锁:
public class die1 {
public Object lock1 = new Object();
public Object lock2 = new Object();
public void die1Mthod1(){
synchronized(lock1){
synchronized(lock2){
}
}
}
public void die1Mthod2(){
synchronized(lock2){
synchronized(lock1){
}
}
}
}
对于上线的类,如果只有一个线程串行执行的话,绝对不会发生死锁,但是如果超过两个线程,并且等待时间足够长,那么当一个线程在执行方法一同时另一个线程在执行方法2的时候就会出现死锁。因为两个线程持有对方需要的资源,程序会暂停执行,发生死锁。
2.动态的死锁:
public class die2 {
public static Object fromAccount = new Object();
public static Object toAccount = new Object();
public static void transfer(Object fromAccout,Object toAccount,int money) throws InterruptedException{
/*if(fromAccout.hashCode() < toAccount.hashCode()){
Thread.sleep(3000);
synchronized(fromAccout){
synchronized(toAccount){
System.out.println(Thread.currentThread().getName() + "交易金额+:" + money);
}
}
}
if(fromAccout.hashCode() > toAccount.hashCode()){
Thread.sleep(3000);
synchronized(toAccount){
synchronized(fromAccout){
System.out.println(Thread.currentThread().getName() + "交易金额! :" + money);
}
}
}*/
/* 未定义之前的锁顺序
*/
//Thread.sleep(3000);
synchronized(fromAccout){
Thread.sleep(1000); (1)
synchronized(toAccount){
System.out.println("交易金额:" + money);
}
}
}
public static void main(String[] args){
Thread thead1 = new Thread(new Runnable(){
@Override
public void run() {
try {
die2.transfer(die2.fromAccount, die2.toAccount, 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thead2 = new Thread(new Runnable(){
@Override
public void run() {
try {
die2.transfer(die2.toAccount, die2.fromAccount, 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thead2.start();
thead1.start();
}
}
上面所示,在一处是为了让线程一在获得了fromAccount这个锁后,延迟获得toAccount锁,让线程2获得,这样就能产生死锁,所以这样的代码虽然避免了死锁1的情况,但是会在程序运行的时候因为参数的原因导致了死锁。注释部分为解决方法。
3.协作对象的死锁:
public class die3 {
static class die33{
public static synchronized void method11() throws InterruptedException{
//Thread.sleep(1000);
method1();
System.out.println("method11");
}
public static synchronized void method22(){
System.out.println("method22");
}
}
//注意:没有休眠,不会死锁,但是会引起栈的溢出
public static synchronized void method1(){
System.out.println("method1");
}
public static synchronized void method2() throws InterruptedException{
//Thread.sleep(1000);
die33.method22();
System.out.println("method2");
}
public static void main(String[] args){
Thread thead1 = new Thread(new Runnable(){
@Override
public void run() {
try {
die3.method2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thead2 = new Thread(new Runnable(){
@Override
public void run() {
try {
die3.die33.method11();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thead2.start();
thead1.start();
}
}
因为虽然没有锁的顺序,但是在调用方法上,两个线程有可能会持有,两个不同的对象的锁,而有等待对方的锁,从而产生死锁。
解决死锁的办法:
1)接触顺序死锁的关系:如果die2的注释。
2)开放调用:在die3中,使用synchronized(this){}用于保护仅需要同步的操作,减少持有锁的时间。
3)使用定时锁,一定时间不能获得锁(tryLock),那么他将会放弃。
其他危险:
1)避免使用线程(api)的优先级,那会增加对平台的依赖,不正确的优先级,导致一些线程得不到cpu时间片,导致饥饿,所以使用java默认优先级。
2)线程重复执行相同的操作,而且总会失败,导致虽然没有阻塞进程,但是程序仍然没有执行,例如,两台机器同时发出消息,都发现了冲突,那么为失败,但是它们又会都等待相同的时间,从而导致程序一直失败的执行下去,所以可以让他们等待随机的时间,或者回退即可解决这样的活锁。