安全停止线程

时间:2018-10-05 12:52:39

标签: java multithreading

我上了一堂课

@Component
public MyClass {
   private volatile boolean stopped = false; 

   public void verification() throws Exception {

        Thread kpiAllThread = getKPIAllThread();

        try {
            for (int i = 0; i < poolSize; i++) {
                execDispatcher.put(processExecutor.submit(getCheckValuesInKPIConsumerTask(workingQueue)));
            }
            kpiAllThread.start();
        } finally {
            waitFinished();
        }
    }

    public void setStop(bolean stopped) {
         this.stopped = stopped;
    }

    private Thread getKPIAllThread() {
        return new Thread(() -> {
            try {
                LOG.debug("KPIAllThread started!");
                dao.getKpiAll(workingQueue);
                for (int i = 0; i < poolSize; i++) {
                    workingQueue.put(() -> true);
                }
            } catch (Exception ex) {
                LOG.error("KPIAllThread exception: ", ex);
            } finally {
                LOG.error("KPIAllThread finished!");
            }
        });
    }
}

该类启动生产者线程getKPIAllThread。他从db获取数据并放入BlockingQueue

方法getKpiAll像这样:

public void getKpiAll(final BlockingQueue<KeyPropertyIndex> kpiData) throws Exception {
        LOG.debug("Starting getKpiAll");
        try (final Connection con = dataSource.getConnection();
             final Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
            stmt.setFetchSize(Integer.MIN_VALUE);

            try (final ResultSet rs = stmt.executeQuery(sqlGetAllkpi)) {
                while (rs.next()) {
                    kpiData.put(new KeyPropertyIndexData(rs.getLong(1), rs.getString(2)));
                }
            }
            LOG.debug("Finished get getKpiAll");
        } catch (Exception ex) {
            throw ex;
        }
    }

还有一个变量stopped,可以从外部设置为true。这样做时如何安全地停止线程?这样就关闭了与数据库的所有连接并成功完成了线程?

1 个答案:

答案 0 :(得分:2)

停止线程最干净,最安全的规则是,在线程中运行的代码应定期检查条件(例如boolean shouldExit())。当代码检测到这种情况为真时,应停止执行操作并终止。

在线程中运行的代码应相当频繁地检查此条件,以便可以合理快速地做出反应。根据经验,设置此条件后,线程应退出不到一秒钟。该检查通常看起来像if (shouldExit()) break,它在for循环中的某个位置上遍历池大小。但是,dao.getKpiAll(workingQueue)看起来可能很长,因此您可以在getKpiAll内放置更多支票。

当您进行此检查时,必须确保每次条件为真时,您的代码将干净退出。例如,您可以使用finally块来关闭任何连接等。如果在getKpiAll期间发生这种情况,甚至没有理由继续for循环来处理项目,依此类推。 / p>

有时,这可能会变得更加棘手-例如,当线程正在等待网络操作时,您可能需要关闭网络套接字或类似的东西才能中断它。无论如何,请避免使用Thread.stop()Thread.interrupt()-请参阅文档,了解它们为何有问题。

如果执行这样的操作,则可以随时从线程外部设置条件以请求终止线程。您可以进行类似void requestExit()的操作,并在其中设置一个布尔变量。调用requestExit()之后,您以适当的超时调用Thread.join()来等待线程处理其事务,检查条件并退出。同样,根据经验,将超时设置为线程的最长响应时间的3到10倍。

看来您已经有setStopped(boolean stopped)用于此目的,但您没有检查它。首先,我将删除参数stopped,因为将false传递给它没有意义。其次,您需要如上所述添加检查。您可能想使此变量对dao可见-请记住,将其公开为同步布尔方法比将其公开为布尔字段要好得多。