我正在实现一个程序,其中包含不同的任务,并且都已实现Runnable。例如有一个工作在数据库上的任务,并将一些元组发送到同步的共享内存,随后,还有另一个线程检查共享内存并将消息发送到队列。而且,这两个线程在无限的while循环中迭代。
我已经使用了fixedThreadPool执行这些线程。
问题在于,有时程序控制保留在第一个运行线程中,而第二个线程则永远没有机会进入其运行状态。
以下是与我类似的示例代码:
public class A implements Runnable {
@Override
public void run() {
while(true) {
//do something
}
}
}
public class B implements Runnable {
@Override
public void run() {
while(true) {
//do something
}
}
}
public class Driver {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
A a = new A();
executorService.execute(a);
B b = new B();
executorService.execute(b);
}
}
我也做了一些棘手的事情,在短时间运行后,使第一个线程睡眠一秒钟。结果,它使第二个线程找到运行的机会。但是,对于这个问题是否有格式正确的解决方案?您认为问题出在哪里?
答案 0 :(得分:3)
这是生产者/消费者模式的一个很好的例子。有很多实现方法。这是一个使用wait/notify
模式的幼稚实现。
public class A implements Runnable {
private Queue<Integer> queue;
private int maxSize;
public A(Queue<Integer> queue, int maxSize) {
super();
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.size() == maxSize) {
try {
System.out.println("Queue is full, " + "Producer thread waiting for "
+ "consumer to take something from queue");
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
Random random = new Random();
int i = random.nextInt();
System.out.println("Producing value : " + i);
queue.add(i);
queue.notifyAll();
}
}
}
}
public class B implements Runnable {
private Queue<Integer> queue;
public B(Queue<Integer> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
System.out.println("Queue is empty," + "Consumer thread is waiting"
+ " for producer thread to put something in queue");
try {
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
System.out.println("Consuming value : " + queue.remove());
queue.notifyAll();
}
}
}
}
这里很热,我们进行了设置。
public class ProducerConsumerTest {
public static void main(String[] args) {
Queue<Integer> buffer = new LinkedList<>();
int maxSize = 10;
Thread producer = new Thread(new A(buffer, maxSize));
Thread consumer = new Thread(new B(buffer));
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(producer);
executorService.submit(consumer);
}
}
在这种情况下,Queue
充当共享内存。您可以用其他任何适合您需要的数据结构代替它。这里的窍门是您必须仔细地在线程之间进行协调。这就是您上面的实现所缺少的。
答案 1 :(得分:0)
我知道这听起来可能很激进,但是异步代码库的非框架部分应该尝试避免使用while(true)
手工编码的循环,而应将其建模为(可能是自我重新安排的)回调到执行器中
这可以更公平地利用资源,最重要的是每个项目的监视工具。
当代码对延迟的要求不严格(或仅在原型设计时),最简单的方法是使用Executors
和可能的CompletableFuture
s。
class Participant implements Runnable {
final Executor context;
@Override
public void run() {
final Item work = workSource.next();
if (workSource.hasNext()) {
context.execute(this::run);
}
}
}