我在Java中使用wait()
和notify()
编写生产者和消费者代码。
Thread-0已创建并在produce()
上调用,并且创建了Thread-1并在consume()
上调用。
public class Processor {
private volatile List<Integer> list = new ArrayList<>();
private final int MAX_CAPACITY = 5;
Object lock = new Object();
public void produce() throws InterruptedException {
while (true) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int random = new Random().nextInt(100);
list.add(random);
System.out.println("Added to list:" + random);
lock.notify();
}
}
}
public void consume() throws InterruptedException {
while (true) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
synchronized (lock) {
lock.wait();
}
}
synchronized (lock) {
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}
}
问题是在执行期间,程序在produce()
之后停止:
List is empty!! Consumer is Waiting...
Added to list:22
Added to list:45
Added to list:72
Added to list:91
Added to list:51
List is full! Producer is Waiting....
我无法理解这里的问题是什么。我以某种方式弄清楚将while
synchronized
produce()
中的代码包裹在consume()
和produce()
中,以解决问题。
synchronized (lock) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
lock.wait();
}
consume
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
}
public class App {
public static void main(String[] args) {
final Processor processor = new Processor();
Runnable r1 = new Runnable() {
@Override
public void run() {
try {
processor.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
try {
processor.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
这是什么问题?这是线程饥饿还是死锁的情况?
编辑:致电课程:
$.ajax({
type: 'GET',
url: '/first_line_benefits',
data: { tablename: tablename },
success: function(data) {
var dataArray = data[0];
var ret = "";
for (var j = 1; j < dataArray.length; j++) {
ret += "<div class=\"filter_button col-md-4\">";
ret += "<select class=\"selectpicker\" multiple><option value='0' selected>Nothing selected</option>";
for (var i = 0; i < dataArray[j].length; i++)
{
ret += "<option>"+ dataArray[j][i] + "</option>";
}
ret += "</select></div>";
}
$('#filter').html(ret);
}
});
答案 0 :(得分:6)
执行list.size()
时,它不是线程安全的,并且没有保证,您将看到另一个线程中的值已更改。如果它检测到你没有在该线程中更改它,JIT甚至可以内联该值。
通过将synchronized
块放在循环外部,可确保值的变化可见(因为它也位于while(true)
循环内。
答案 1 :(得分:1)
使用synchronized
外部循环创建read barrier。因此,生产者/消费者将看到您正在检查list
的最新list.size()
内部循环。这就是为什么它会在while
块内移动synchronized
循环后才能生效。
在您的情况下,我还建议您在生产者/消费者中使用单个同步块。
例如,在您的实现中,如果list.size() == 0
对于使用者变为false
,它将释放对lock
对象的锁定,然后在下一个语句中尝试再次重新获取锁定以消耗数据,这是不必要的,效率低下的。应该是这样的:
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
lock.wait();
}
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}