同步方法以避免死锁

时间:2015-06-04 19:37:34

标签: java multithreading deadlock synchronized locks

我有两种同步方法,我使用中介设计模式。 我试图避免死锁,这是(据我所知),例如当线程锁定变量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();
    }
}

4 个答案:

答案 0 :(得分:3)

  

例如,当一个线程锁定变量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再次运行时,它会在尝试锁定时立即被阻止由{B}拥有的lock2。任何一个线程都不能从那一点继续。

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:-1)

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

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

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

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());
}

}