【问题标题】:Synchronized methods to avoid deadlock避免死锁的同步方法
【发布时间】:2015-06-04 19:37:34
【问题描述】:

我有两个同步方法,我正在使用中介者设计模式。 我试图避免死锁,这是(据我了解)例如,当线程锁定变量 res1 但需要锁定变量 res2 时。另一个线程需要 res1 的锁,但拥有 res2 的锁 - 导致死锁,对吗?

假设我对死锁的理解是正确的,那么我的问题是我是否解决了这段代码中的死锁问题?

我有两个同步方法和两个线程。

public class Producer extends Thread {
    private Mediator med;
    private int id;
    private static int count = 1;

    public Producer(Mediator m) {
        med = m;
        id = count++;
    }

    public void run() {
        int num;
        while(true) {
            num = (int)(Math.random()*100);
            med.storeMessage(num);
            System.out.println("P-" + id + ": " + num);
        }
    }
}

public class Consumer extends Thread {
    private Mediator med;
    private int id;
    private static int count = 1;

    // laver kopling over til mediator
    public Consumer(Mediator m) {
        med = m;
        id = count++;
    }

    public void run() {
        int num;
        while(true) {
            num = med.retrieveMessage();
            System.out.println("C" + id + ": " + num);
        }
    }
}

public class Mediator {
    private int number;
    private boolean slotFull = false;

    public synchronized void storeMessage(int num) {
        while(slotFull == true) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }  
        slotFull = true;
        number = num;
        notifyAll();
    }

    public synchronized int retrieveMessage() {
        while(slotFull == false) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        slotFull = false;
        notifyAll();
        return number;
    }
}

public class MediatorTest {
    public static void main(String[] args) {
        Mediator mb = new Mediator(); 
        new Producer(mb).start();
        new Producer(mb).start();
        new Producer(mb).start();

        new Consumer(mb).start();
        new Consumer(mb).start();
    }
}

【问题讨论】:

    标签: java multithreading deadlock synchronized locks


    【解决方案1】:

    例如,当一个线程对变量 res1 进行了锁定但需要对变量 res2 进行锁定时

    重要的不是有两个变量,重要的是必须有两个(或更多)

    名称“res1”和“res2”意在暗示两个资源,每个资源可能有一个或多个变量,并且每个都有自己的锁。这就是你遇到麻烦的地方:

    final Object lock1 = new Object();
    final Object lock2 = new Object();
    
    public void method1() {
        synchronized (lock1) {
            // Call Thread.sleep(1000) here to simulate the thread losing its time slice.
            synchronized(lock2) {
                doSomethingThatRequiresBothLocks
            }
        }
    }
    
    public void method2() {
        synchronized (lock2) {
            // Do the same here 'cause you can't know which thread will get to run first.
            synchronized(lock1) {
                doSomethingElseThatRequiresBothLocks()
            }
        }
    }
    

    如果线程 A 调用 method1(),它在成功锁定 lock1 之后但之前丢失其时间片(即转为运行)的可能性非常小锁定lock2

    然后,当线程 A 等待再次运行时,线程 B 调用 method2()。线程 B 将能够锁定lock2,但随后由于lock1 被线程 A 锁定而卡住了。此外,当线程 A 再次运行时,它会在尝试锁定 lock2 时立即被阻塞由线程 B 所有。两个线程都无法从该点继续。


    在实际代码中,它从未如此明显。当它发生在现实生活中时,通常是因为来自两个或多个不同模块的代码之间的一些不可预见的交互,这些模块甚至可能不知道彼此,但它们访问相同的公共资源。

    【讨论】:

      【解决方案2】:

      您对基本死锁问题的理解是正确的。关于你解决死锁问题的有效性的第二个问题,你只有一个锁,所以我默认说“是”,因为你描述的死锁在这种情况下是不可能的

      【讨论】:

      • 谢谢 :) 几天后我参加了考试,我正计划描述如何解决死锁。它需要良好的设计,但我认为它看起来像上面提到的代码。你知道如何解决死锁吗?我在想也许是理论上的解释?
      【解决方案3】:

      我同意@ControlAltDel 所说的。你对僵局的理解与我的一致。虽然有几种不同的方式可以表现出死锁,但您所描述的方式 - 所涉及的线程(方法)不一致地获取多个监视器会导致死锁。

      另一种方法是(例如)在持有锁的同时睡觉。当您正确编码时,当生产者发现 slotFull = true 时,它会等待,放弃锁定,因此另一个线程(消费者,与生产者共享 Mediator 的同一实例)可以取得进展可能会导致该线程在收到通知后也取得进展。如果您选择调用Thread.sleep() 代替(天真地希望有人会在条件为假时导致睡眠结束),那么它将导致死锁,因为该线程正在睡眠,仍然持有锁,拒绝访问其他线程。

      【讨论】:

        【解决方案4】:

        每个对象都有一个锁,当您使用同步关键字时,它会限制多个线程访问同一块代码或方法。

        来到你的问题,它不会死锁。

        如果一个类中有两个独立的属性被多个线程共享,则必须同步对每个变量的访问,但是如果一个线程正在访问其中一个属性,另一个线程同时访问另一个属性,则没有问题.

        class Cinema {
        private long vacanciesCinema1;  private long vacanciesCinema2;
        private final Object controlCinema1, controlCinema2;
        public Cinema() {
            controlCinema1 = new Object();
            controlCinema2 = new Object();
            vacanciesCinema1 = 20;
            vacanciesCinema2 = 20;
        }
        public boolean sellTickets1(int number) {
            synchronized (controlCinema1) {
                if (number < vacanciesCinema1) {
                    vacanciesCinema1 -= number;
                    return true;
                } else {
                    return false;
                }
            }
        }
        public boolean sellTickets2(int number) {
            synchronized (controlCinema2) {
                if (number < vacanciesCinema2) {
                    vacanciesCinema2 -= number;
                    return true;
                } else {
                    return false;
                }
            }
        }
        public boolean returnTickets1(int number) {
            synchronized (controlCinema1) {
                vacanciesCinema1 += number;
                return true;
            }
        }
        public boolean returnTickets2(int number) {
            synchronized (controlCinema2) {
                vacanciesCinema2 += number;
                return true;
            }
        }
        public long getVacanciesCinema1() {
            return vacanciesCinema1;
        }
        
        public long getVacanciesCinema2() {
            return vacanciesCinema2;
        }
        

        }

        class TicketOffice1 implements Runnable {
        private final Cinema cinema;
        
        public TicketOffice1(Cinema cinema) {
            this.cinema = cinema;
        }
        @Override
        public void run() {
            cinema.sellTickets1(3);
            cinema.sellTickets1(2);
            cinema.sellTickets2(2);
            cinema.returnTickets1(3);
            cinema.sellTickets1(5);
            cinema.sellTickets2(2);
            cinema.sellTickets2(2);
            cinema.sellTickets2(2);
        }
        

        }

        public class CinemaMain {
        public static void main(String[] args) {
            Cinema cinema = new Cinema();
            TicketOffice1 ticketOffice1 = new TicketOffice1(cinema);
            Thread thread1 = new Thread(ticketOffice1, "TicketOffice1");
            TicketOffice2 ticketOffice2 = new TicketOffice2(cinema);
            Thread thread2 = new Thread(ticketOffice2, "TicketOffice2");
            thread1.start();
            thread2.start();
            try {
                thread1.join();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("Room 1 Vacancies: %d\n", cinema.getVacanciesCinema1());
            System.out.printf("Room 2 Vacancies: %d\n", cinema.getVacanciesCinema2());
        }
        

        }

        【讨论】:

        • 同步不能防止死锁。相反,在不采取适当预防措施的情况下进行同步可能会导致死锁。
        • 是的,请谨慎使用同步关键字。
        猜你喜欢
        • 1970-01-01
        • 2021-11-09
        • 2017-01-05
        • 1970-01-01
        • 1970-01-01
        • 2017-12-17
        • 1970-01-01
        • 1970-01-01
        • 2020-10-25
        相关资源
        最近更新 更多