Java死锁问题

死锁定义

1)进程死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

2)线程死锁是指由于两个或者两个以上的线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。

产生死锁的四个必要条件

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程(或线程,以下同)占用。如果此时还有其它进程(线程)请求资源,则请求者只能等待,直至占有资源的进程(线程)用毕释放。如:当线程A进入synchronized,持有一个对象(Monitor)后,其他线程不能再持有这个对象了。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

死锁-Java代码实例:

public class DeadLock {

	public final static Object A = new Object();
	public final static Object B = new Object();
    public final static Object C = new Object();

	public static void main(String[] args) throws InterruptedException {

		Thread thread01 = new Thread(() -> {
				synchronized (A) {//线程thrad-1持有A对象
					System.out.println(Thread.currentThread().getName()+" lock A");
					try {
						Thread.sleep(2000L);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (B) {//保持持有A对象,请求持有B对象
						System.out.println(Thread.currentThread().getName()+" lock B");
					}
				}
		},"thrad-1");
	
		Thread thread02 = new Thread(() -> {
				synchronized (B) {
					System.out.println(Thread.currentThread().getName()+" lock B");
					try {
						Thread.sleep(2000L);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (A) {//synchronized (C){打印对象C}
						System.out.println(Thread.currentThread().getName()+" lock A");
					}
				}
		},"thrad-2");

		thread01.start();//执行线程thrad-1
		//thread01.join();//阻塞,等待线程thrad-1执行完,再执行后面的代码逻辑。即:先让线程线程thrad-1执行完
		thread02.start();//执行线程thrad-2
	}

}

死锁的效果:

Java程序不结束,满足死锁4个条件,形成死锁;

Java死锁问题_第1张图片

避免死锁的方法

只要破坏产生死锁的死锁的4个必要条件中的一个,即可解决死锁问题;

1)破坏互斥条件不剥夺条件,对sychronized来说,是JVM底层设定的,每个对象都有一个monitor(监视器),在同步操作中,一个线程(或进程)持有一个对象的monitor后,有monitorenter操作,同步方法结束或异常时,有monitorexit操作。所以我们是破坏不了这个2个条件的;如果不是sychronized,如通过Lock完成的互斥条件不剥夺条件,就可以更改;

2)破坏请求和保持条件,常见的操作有:加锁顺序(指定加锁的顺序或者线程执行的顺序)、加锁时限(对加锁的对象增加超时限制,避免一直占用);

3)破坏环路等待条件:死锁检测:多线程对期望持有的锁对象不能形成相互(环状)依赖的情况。

如,上述死锁代码中;线程01持有对象A后,去请求持有对象B;

                                    线程02持有对象B后,去请求持有对象A,-------多线程持有的对象A、B是相互依赖的;造成死锁;

     如果是这样,线程01持有对象A后,去请求持有对象B;

                           线程02持有对象B后,去请求持有对象C,  ------------A、B、C之间没有相互依赖,不会死锁;

你可能感兴趣的:(Java死锁问题)