我正在编写Java软件,它有一个单独的线程,可以监听被按下的外部按钮。如果按下按钮,则线程通知其他线程,否则它只是休眠。
我的模型是使用中断驱动的设计。理想情况下我想做 只要没有按下按钮,线程就会休眠。当按下按钮时,我希望线程做一些工作然后再回去睡觉。
任何人都可以确认/更正以下实施吗?
public abstract class AbstractAssembler<V extends AbstractValue, E extends AbstractEntity>
{
public final E transformToEntity(V value)
{
if (value == null)
{
return null;
}
E entity = createEntity(value);
entity.setUniqueId(value.getUniqueId());
entity.setNominalAmountValue(value.getNominalAmountValue());
return entity;
}
/**
* @return
* Appropriate entity object, with the fields not common to all AbstractEntity
* already set
*/
protected abstract E createEntity(V value);
}
此外,在每个按钮点击终端后,我收到一条消息
public class FirstAssembler extends AbstractAssembler<FirstValue, FirstEntity>
{
@Override
protected FirstEntity createEntity(FirstValue value)
{
FirstEntity entity = new FirstEntity();
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
这条消息意味着什么(例如,如果我发现异常,为什么会打印出来)?我可以避免终端打印吗?
答案 0 :(得分:7)
如果您使用例外来控制程序流,通常会将其视为代码气味。
此问题的正确解决方案是使用事件处理程序从中读取的BlockingQueue
个事件。这通常被称为生产者/消费者。
public class TwoThreads {
public static void main(String args[]) throws InterruptedException {
System.out.println("TwoThreads:Test");
new TwoThreads().test();
}
// The end of the list.
private static final Integer End = -1;
static class Producer implements Runnable {
final BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 0; i < 1000; i++) {
queue.add(i);
Thread.sleep(1);
}
// Finish the queue.
queue.add(End);
} catch (InterruptedException ex) {
// Just exit.
}
}
}
static class Consumer implements Runnable {
final BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
boolean ended = false;
while (!ended) {
try {
Integer i = queue.take();
ended = i == End;
System.out.println(i);
} catch (InterruptedException ex) {
ended = true;
}
}
}
}
public void test() throws InterruptedException {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Thread pt = new Thread(new Producer(queue));
Thread ct = new Thread(new Consumer(queue));
// Start it all going.
pt.start();
ct.start();
// Wait for it to finish.
pt.join();
ct.join();
}
}
不要让自己感到困惑的是这是多少代码 - 大多数只是包装。核心功能是:
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Thread pt = new Thread(new Producer(queue));
Thread ct = new Thread(new Consumer(queue));
queue.add(i);
while (!ended) {
try {
Integer i = queue.take();
请注意,take
此处将阻止,直到事件发布或发生中断。
答案 1 :(得分:3)
您可以使用
Thread.sleep(Long.MAX_VALUE); // more than the life of your computer
或
synchronized(this) {
wait();
}
或中断唤醒,但不会抛出异常
LockSupport.park();
然而,一个更优雅的解决方案可能是使用ExecutorService被设计成一个睡眠线程池,当你让它工作时它就会被唤醒。
ExecutorsService executor = Executors.newSingleThreadExecutor();
// when you want it to do something
executor.submit(this::process);
注意:您应该考虑如何处理异常。在您的问题的示例中,异常或错误将终止该线程,它将停止运行。在我的例子中,它不会杀死线程池,但实际的异常可能会丢失。因此我建议你这样写。
executor.submit(() -> {
try {
process();
} catch(Throwable t) {
LOGGER.warning(t);
}
});
注意:不要只是调用process
,而是必须弄清楚你想做什么,你可以像这样写。
doSomething(string,number,pojo);
通过这种方式,您可以看到您期望后台线程处理哪些数据。
为了比较,这里是使用当前线程作为生产者和执行者服务的TwoThread示例。
public class TwoThreadsJava5 {
public static void main(String args[]) throws InterruptedException {
System.out.println("TwoThreads:Test - Java 5.0 style");
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++) {
final int finalI = i;
executor.submit(() -> {
try {
System.out.println(finalI);
} catch (Throwable t) {
t.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
}
在Java 8中你可以写
public class TwoThreadsJava8 {
public static void main(String args[]) throws InterruptedException {
System.out.println("TwoThreads:Test - Java 8 style");
IntStream.range(0, 1000)
.parallel()
.forEach(System.out::println);
}
}