我正在开发一个循环缓冲区,它有两个线程:使用者和生产者。我在Thread.yield中使用活动等待。我知道用信号量可以做到这一点,但是我想要没有信号量的缓冲区。
两者都有一个共享变量:bufferCircular。
当缓冲区中没有充分的有用信息时,producer在数组的位置p中写入数据,而在数组的位置c中有一些有用的信息consumer读取数据。来自BufferCircular的变量BufferCircular是尚未被读取的值数据的数量。
这个程序运行了相当好的9/10倍。然后,有时,在显示屏幕上的最后一个元素(循环编号500个)之前,它会在无限循环中得到stucks,或者就是不显示任何元素。
我想可能是liveLock,但我找不到错误。
共享变量
public class BufferCircular {
volatile int[] array;
volatile int p;
volatile int c;
volatile int nElem;
public BufferCircular(int[] array) {
this.array = array;
this.p = 0;
this.c = 0;
this.nElem = 0;
}
public void writeData (int data) {
this.array[p] = data;
this.p = (p + 1) % array.length;
this.nElem++;
}
public int readData() {
int data = array[c];
this.c = (c + 1) % array.length;
this.nElem--;
return data;
}
}生产者线程
public class Producer extends Thread {
BufferCircular buffer;
int bufferTam;
int contData;
public Productor(BufferCircular buff) {
this.buffer = buff;
this.bufferTam = buffer.array.length;
this.contData = 0;
}
public void produceData() {
this.contData++;
this.buffer.writeData(contData);
}
public void run() {
for (int i = 0; i < 500; i++) {
while (this.buffer.nElem == this.bufferTam) {
Thread.yield();
}
this.produceData();
}
}
}消费线程
public class Consumer extends Thread {
BufferCircular buffer;
int cont;
public Consumer(BufferCircular buff) {
this.buffer = buff;
this.cont = 0;
}
public void consumeData() {
int data = buffer.readData();
cont++;
System.out.println("data " + cont + ": " + data);
}
public void run() {
for (int i = 0; i < 500; i++) {
while (this.buffer.nElem == 0) {
Thread.yield();
}
this.consumeData();
}
}
}主
public class Main {
public static void main(String[] args) {
Random ran = new Random();
int tamArray = ran.nextInt(21) + 1;
int[] array = new int[tamArray];
BufferCircular buffer = new BufferCircular(array);
Producer producer = new Producer (buffer);
Consumer consumer = new Consumer (buffer);
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
System.err.println("Error with Threads");
e.printStackTrace();
}
}
}欢迎任何帮助。
发布于 2015-05-03 12:25:55
这里的问题是,您的BufferCircular方法对竞争条件很敏感。以writeData()为例。它以3个步骤执行,其中一些步骤也不是原子的:
this.array[p] = data; // 1
this.p = (p + 1) % array.length; // 2 not atomic
this.nElem++; // 3 not atomic假设两个线程同时进入writeData()。在步骤1中,它们都具有相同的p值,并且都重写了array[p]值。现在,array[p]被重写了两次,第一个线程必须写的数据丢失了,因为第二个线程在后面写入了相同的索引。然后他们执行步骤2-而且结果是不可预测的,因为p可以增加1或2 (p = (p + 1) % array.length由3个操作组成,其中线程可以交互)。然后,步骤3. ++操作符也不是原子操作:它在幕后使用两个操作。因此,nElem也会增加1或2。
所以我们有完全不可预测的结果。这会导致你的程序执行不力。
最简单的解决方案是将readData()和writeData()方法序列化。为此,请声明它们为synchronized
public synchronized void writeData (int data) { //...
public synchronized void readData () { //...如果只有一个生产者和一个使用者线程,则在涉及nElem的操作中可能会出现争用条件。解决方案是使用AtomicInteger而不是int
final AtomicInteger nElem = new AtomicInteger();并使用它的incrementAndGet()和decrementAndGet()方法。
https://stackoverflow.com/questions/30013393
复制相似问题