Java:如何在中断后将线程重新置于休眠状态?

时间:2016-02-04 13:29:16

标签: java multithreading

我正在编写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;
    }
}

这条消息意味着什么(例如,如果我发现异常,为什么会打印出来)?我可以避免终端打印吗?

2 个答案:

答案 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并在两个线程之间共享。

    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);
    }
}