首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java并发死锁

Java并发死锁
EN

Stack Overflow用户
提问于 2022-05-06 18:05:05
回答 2查看 33关注 0票数 1

运行Main.main()方法似乎出现了死锁。我发现,如果用notify()替换为notifyAll(),就可以修复它。但是为什么呢?最坏的情况不应该总是被称为“拖线到另一个懒散的线程”吗?

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        Table table = new Table(3);
        new MakerThread("MakerThread-1", table, 8931415L).start();
        new MakerThread("MakerThread-2", table, 314144L).start();
        new MakerThread("MakerThread-3", table, 42131415L).start();
        new EaterThread("EaterThread-1", table, 6313L).start();
        new EaterThread("EaterThread-2", table, 8536313L).start();
        new EaterThread("EaterThread-3", table, 35256313L).start();
        new LazyThread("LazyThread-1", table).start();
        new LazyThread("LazyThread-2", table).start();
        new LazyThread("LazyThread-3", table).start();
        new LazyThread("LazyThread-4", table).start();
        new LazyThread("LazyThread-5", table).start();
        new LazyThread("LazyThread-6", table).start();
        new LazyThread("LazyThread-7", table).start();
    }
}
代码语言:javascript
复制
public class Table {
    private final String[] buffer;
    private int tail;
    private int head;
    private int count;

    public Table(int count) {
        this.buffer = new String[count];
        this.tail = 0;
        this.head = 0;
        this.count = 0;
    }

    public synchronized void put(String cake) throws InterruptedException {
        while (count >= buffer.length) {
            wait();
        }
        System.out.println(Thread.currentThread().getName() + " puts " + cake);
        buffer[tail] = cake;
        tail = (tail + 1) % buffer.length;
        count++;
        notify();
    }

    public synchronized String take() throws InterruptedException {
        while (count <= 0) {
            wait();
        }
        String cake = buffer[head];
        head = (head + 1) % buffer.length;
        count--;
        System.out.println(Thread.currentThread().getName() + " takes " + cake);
        notify();
        return cake;
    }
}
代码语言:javascript
复制
public class EaterThread extends Thread {
    private final Random random;
    private final Table table;

    public EaterThread(String name, Table table, long seed) {
        super(name);
        this.random = new Random(seed);
        this.table = table;
    }

    @Override
    public void run() {
        try {
            while (true) {
                String cake = table.take();
                Thread.sleep(random.nextInt(1000));
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
代码语言:javascript
复制
public class MakerThread extends Thread {
    private final Random random;
    private final Table table;
    private static int id = 0;

    public MakerThread(String name, Table table, long seed) {
        super(name);
        this.random = new Random(seed);
        this.table = table;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(random.nextInt(1000));
                String cake = " Cake No." + nextId() + " by " + getName() + " ]";
                table.put(cake);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static synchronized int nextId() {
        return ++id;
    }
}
代码语言:javascript
复制
public class LazyThread extends Thread {
    private final Table table;

    public LazyThread(String name, Table table) {
        super(name);
        this.table = table;
    }

    @Override
    public void run() {
        while (true) {
            try {
                synchronized (table) {
                    table.wait();
                }
                System.out.println(getName() + " is notified");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

控制台输出

EN

回答 2

Stack Overflow用户

发布于 2022-05-06 18:22:50

您需要notifyAll而不是通知。否则,一个制造商的通知可能会唤醒另一个制造商,并使整个事件陷入僵局。对懒惰的人也是。

这样做的一个更好的方法是,使用一个锁的制作者和一个锁的懒人(接受者),然后你可以使用通知,当东西被添加或删除。

代码语言:javascript
复制
public synchronized void put(String cake) throws InterruptedException {
    while (count >= buffer.length) {
        wait();
    }
    System.out.println(Thread.currentThread().getName() + " puts " + cake);
    buffer[tail] = cake;
    tail = (tail + 1) % buffer.length;
    count++;
    notifyAll();
}

public synchronized String take() throws InterruptedException {
    while (count <= 0) {
        wait();
    }
    String cake = buffer[head];
    head = (head + 1) % buffer.length;
    count--;
    System.out.println(Thread.currentThread().getName() + " takes " + cake);
    notifyAll();
    return cake;
}
票数 1
EN

Stack Overflow用户

发布于 2022-05-06 18:59:03

正如医生所说:

公共最终无效通知()

..。如果任何线程正在等待此对象,则选择其中一个线程进行唤醒。这一选择是任意的,由执行决定.

...the唤醒线程在成为锁定此对象的下一个线程时没有任何可靠的特权或缺点。..。

https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#notify()

您绝对可以实现这一点,以便只通知()一个线程。它取决于前一个线程何时释放锁定的对象。如果通知了一个线程,但资源仍然绑定到通知线程,则释放的线程将返回等待状态,之后没有线程被通知。

当您通知所有等待线程时,以及当第一个线程没有得到锁定的对象(因为仍然被通知线程锁定)时,剩下的唤醒线程将试图捕获它。

因此,随着许多唤醒线程的出现,锁定对象被其中之一捕获的可能性要高得多。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72145710

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档