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

发布于 2022-05-06 18:22:50
您需要notifyAll而不是通知。否则,一个制造商的通知可能会唤醒另一个制造商,并使整个事件陷入僵局。对懒惰的人也是。
这样做的一个更好的方法是,使用一个锁的制作者和一个锁的懒人(接受者),然后你可以使用通知,当东西被添加或删除。
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;
}发布于 2022-05-06 18:59:03
正如医生所说:
公共最终无效通知()
..。如果任何线程正在等待此对象,则选择其中一个线程进行唤醒。这一选择是任意的,由执行决定.
...the唤醒线程在成为锁定此对象的下一个线程时没有任何可靠的特权或缺点。..。
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#notify()
您绝对可以实现这一点,以便只通知()一个线程。它取决于前一个线程何时释放锁定的对象。如果通知了一个线程,但资源仍然绑定到通知线程,则释放的线程将返回等待状态,之后没有线程被通知。
当您通知所有等待线程时,以及当第一个线程没有得到锁定的对象(因为仍然被通知线程锁定)时,剩下的唤醒线程将试图捕获它。
因此,随着许多唤醒线程的出现,锁定对象被其中之一捕获的可能性要高得多。
https://stackoverflow.com/questions/72145710
复制相似问题