为什么这段代码总是不产生死锁,想了解Reentrant锁与同步块的工作原理?

问题描述 投票:0回答:1

我有两段代码。第一段使用同步块,它导致了死锁,这是完全可以理解的。

在第二段代码中,我试图重新创建同样的问题,但使用了 Reentrant locks 这次。但第二个代码 在某些情况下不会导致死锁。 有些情况下会出现死锁,控制台中什么也没有打印。

你能解释一下为什么吗?我是不是没有使用 Reentrant locks 正确吗?

导致死锁的代码

package com.practice.multithreading;

public class DeadlockYoutube {

    public static void main(String[] args) {

        final String resource1="Printer";
        final String resource2="Scanner";

        Runnable run1=()->{
            synchronized (resource1) {
                System.out.println(Thread.currentThread().getName()+" : locked-> "+resource1);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(resource2) {
                    System.err.println(Thread.currentThread().getName()+" : locked-> "+resource2);
                }
            }
        };

        Runnable run2=new Runnable(){
            @Override
            public void run() {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread().getName()+" : locked-> "+resource2);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized(resource1) {
                        System.err.println(Thread.currentThread().getName()+" : locked-> "+resource1);
                    }
                }   
            }

        };

        Thread thread1= new Thread(run1);
        thread1.setName("Desktop");
        Thread thread2=new Thread(run2);
        thread2.setName("Laptop");

        thread1.start();
        thread2.start();


    }

}

同样的代码,再入式锁止导致死锁。

package com.practice.multithreading;

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


//not working as expected
public class DeadlockYoutubeReentrantLocks {

    public static void main(String[] args) {

        final String resource1 = "Printer";
        final String resource2 = "Scanner";

        Lock lock1 = new ReentrantLock();
        Lock lock2 = new ReentrantLock();

        Runnable run1 = () -> {
            lock1.lock();
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + " : locked-> " + resource1);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.err.println(Thread.currentThread().getName() + " : locked-> " + resource2);

            lock1.unlock();
            lock2.unlock();

        };

        Runnable run2 = new Runnable() {
            @Override
            public void run() {
                lock2.lock();
                lock1.lock();
                System.out.println(Thread.currentThread().getName() + " : locked-> " + resource2);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.println(Thread.currentThread().getName() + " : locked-> " + resource1);
                lock2.unlock();
                lock1.unlock();
            }

        };

        Thread thread1 = new Thread(run1);
        thread1.setName("Desktop");
        Thread thread2 = new Thread(run2);
        thread2.setName("Laptop");

        thread1.start();
        thread2.start();

    }

}

我试着把锁的顺序颠倒过来,但代码有时会执行得很好,而且 有时会出现僵局,控制台里什么都没有。.

请解释这种行为。

java multithreading deadlock synchronized reentrantlock
1个回答
2
投票

你的两段代码不是 相当.

在第一种情况下,你

  1. 获取资源1的锁
  2. 睡眠
  3. 获取资源2的锁

在第二段代码中(使用锁)

  1. 获取资源1的锁
  2. 获取资源2的锁
  3. 睡眠

通过第二种方式,你是降低了两个线程获取每个资源的锁的概率(线程1获取资源1的锁,线程2获取资源2的锁)。因此,这就降低了死锁的次数比例。


要让第2个片段等同于第1个片段。

Runnable run1 = () -> {
    lock1.lock();
    System.out.println(Thread.currentThread().getName() + " : locked-> " + resource1);
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    lock2.lock(); //<-- Moved here
    System.err.println(Thread.currentThread().getName() + " : locked-> " + resource2);

    lock1.unlock();
    lock2.unlock();

};

Runnable run2 = () -> {
    lock2.lock();
    System.out.println(Thread.currentThread().getName() + " : locked-> " + resource2);
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    lock1.lock(); //<-- Moved here
    System.err.println(Thread.currentThread().getName() + " : locked-> " + resource1);
    lock2.unlock();
    lock1.unlock();
};

这应该会让你的代码死锁,因为经常使用 同步.As 评论者@michalk,这两段代码都容易出现死锁。

© www.soinside.com 2019 - 2024. All rights reserved.