我努力做一个多线程的java程序,但它大部分时间都不起作用。
在这个程序中有4个线程应该在ArrayList上读写值。特别是2个线程(Productors)应该写入值,2个线程(消费者)应该读取产品所写的值。在使用者读取值之后,应该清除该数组。
有一个类(Buffer)应该管理写/读请求。
我是多线程编程的新手,我无法让它正常工作。 有人能帮我吗?这是代码:
主:
public class App {
public static void main(String[] args) {
Buffer m = new Buffer();
Productor p1 = new Productor("Productor 1", m);
Productor p2 = new Productor("Productor 2", m);
Consumer c1 = new Consumer("Consumer 1", m);
Consumer c2 = new Consumer("Consumer 2", m);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p2);
Thread t3 = new Thread(c1);
Thread t4 = new Thread(c2);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
缓冲液:
import java.util.ArrayList;
public class Buffer {
private ArrayList<Integer> array;
private boolean state;
public Buffer() {
array = new ArrayList<>();
state = true;
}
public synchronized void write(int num) {
if (!state) {
try {
wait();
} catch (InterruptedException e) {}
}
array.add(num);
state = false;
notifyAll();
}
public synchronized void read() {
if (state) {
try {
wait();
} catch (InterruptedException e) {}
}
System.out.println(array);
array.clear();
state = true;
notifyAll();
}
}
生产方:
import java.util.Random;
public class Productor implements Runnable {
private final String NAME;
private Buffer market;
private final Random random = new Random();
public Productor(String NAME, Buffer market) {
this.NAME = NAME;
this.market = market;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
int j = random.nextInt(10);
market.write(j);
}
}
}
最后,消费者:
public class Consumer implements Runnable {
private final String NAME;
private Buffer market;
public Consumer(String NAME, Buffer market) {
this.NAME = NAME;
this.market = market;
}
@Override
public void run() {
for (int i = 0; i < 2; i++) {
market.read();
}
}
}
感谢。
答案 0 :(得分:2)
基本问题是:你的两个消费者线程将阅读并清除Buffer
总共4次,你的制作人会将Buffer
的单个值写入总共20次,如果他们可以。
但他们不能。
在写入完成之前,消费者通常会完成4种读取方式,然后阻止写入,因为state == false
。因此,程序通常会在一个或多个生产者被阻止的状态下结束。
但是还有另一个问题。读取和写入都在同一个对象上进行通知,但是如果写入线程唤醒,则无法确定它是否被读取通知,并且因为它没有再次检查状态,它会将更多值写入Buffer
。对于读取,同样如此,通过写入无法确定通知。 您应该始终在检查条件的循环中进行等待,并在条件不成立时再次开始等待。
所以你需要的修复是:
state
上的if检查更改为while循环但是,我想知道除非Buffer
为空,否则制作人无法写作。如果生成器被允许写入,即使Buffer
具有值,修复程序是:
state
检查和等待指令
state
上的if检查更改为读取在这种情况下,读取不确定是否会有更多写入,因为读取操作可能会消耗多个写入值,并且一个或两个消费者线程可能会保持阻塞,因为所有写入在执行第二个循环之前已被占用。为了解决这个问题,我建议你看一下“毒丸”#34;解决方案或确保您的读取只消耗一个值并对齐读写次数。
答案 1 :(得分:0)
您的制作人和消费者计划几乎没有基本问题:
(1)您的消费者运行速度非常快是否等待生产者将元素放在数组
中(2)等待和通知的条件不正确
您可以查看以下更新后的代码:
制作类:
public class Productor implements Runnable {
private final String NAME;
private Buffer market;
private final Random random = new Random();
public Productor(String NAME, Buffer market) {
this.NAME = NAME;
this.market = market;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
int j = random.nextInt(10);
market.write(j);
}
market.setCompleted(true);
}
}
消费者类:
public class Consumer implements Runnable {
private final String NAME;
private Buffer market;
public Consumer(String NAME, Buffer market) {
this.NAME = NAME;
this.market = market;
}
@Override
public void run() {
market.read();
}
}
缓冲类:
public class Buffer {
private ArrayList<Integer> array;
public Buffer() {
array = new ArrayList<>();
}
private volatile boolean completed;
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public synchronized void write(int num) {
if (array.size() > 0) {
try {
wait();
} catch (InterruptedException e) {}
} else {
System.out.print(" adding *** ");
array.add(num);
notifyAll();
System.out.println(num);
}
}
public synchronized void read() {
while(true) {
if(isCompleted()) break;
if (array.size() == 0) {
try {
wait();
} catch (InterruptedException e) {}
} else {
System.out.println(" reading *** "+array);
array.clear();
notifyAll();
}
}
}
}
答案 2 :(得分:0)
我已经努力做一个多线程的java程序,但是它的工作次数最多......
是的,多线程程序很难。线程之间的锁定和数据同步并不简单。
听起来你有答案,但将来你应该考虑使用BlockingQueue<Integer>
。阻塞队列(例如LinkedBlockingQueue
)负责内存同步以及生产者和消费者之间的信令。他们都将共享相同的队列:
final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Producer p1 = new Productor("Productor 1", queue);
Producer p2 = new Productor("Productor 2", queue);
Consumer c1 = new Consumer("Consumer 1", queue);
Consumer c2 = new Consumer("Consumer 2", queue);
有了它,你的制作人会做类似的事情:
queue.put(num);
并且您的消费者会这样做:
int num = queue.take();
您可以通过为LinkedBlockingQueue
构造函数指定数字来限制队列。这样,如果生产者比消费者跑得更快,他们就不会填补记忆。
他们唯一的诀窍就是如何阻止消费者等待物品。在队列中放置特定值有效。完成后,您也可以中断线程。