【问题标题】:Can Thread.yield() sometime lead to Thread being stuck in waiting stateThread.yield() 有时会导致线程卡在等待状态吗
【发布时间】:2022-01-17 11:29:34
【问题描述】:

目前我正在开发一个基于报告生成器的应用程序,作为其中的一部分,当用户登录并尝试下载报告时,他最终会调用以下方法作为流程的一部分

执行查询列表以获取所需数据并返回结果。

注意:某些查询可能需要 40-60 分钟才能执行。

现在我确实遇到了一个奇怪的问题,我可以看到有时在触发以下代码后报告永远不会恢复 [通过日志找到观察结果]。

所以我怀疑 Thread.yield() 是否会导致这种情况。由于所有报告调用此代码流具有相同的线程优先级。

我不确定在这个流程中使用 Thread.yield,因为这是一个遗留应用程序,如果这样的代码会导致线程无限等待状态,谁能指导我?

以下是流程的基本伪代码表示,如果需要任何其他信息,请告诉我。提前致谢

Class A {

void m1(reportName) {
 try {

// an unique request id gets created for each report generation request
       Callable<ReportDownload>  = new Download(request, requestid);
       FutureTask<ReportDownload> futureTask = new FutureTask<>(download);
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(futureTask);
        downloadResponse = futureTask.get();
         executorService.shutdown();download
     } catch (Throwable drt) {
        // logs 
    } finally {
         // does terminal operations
   }
 }
Class Download {

  genrate(reortName) {
    List sqllist = // fecth all 3 queries related to connector and store in lis
    runQueries(sqllist)
  }
 
 //  the list sqllist will always have 3 queries and these 3 queries will differ for each and every report
 
 List runQueries(sqllist) {
 
 
 List list = new ArrayList() // used to store result of each query
 for (int i = 0; i != sqllist.size(); ++i) {
       Thread.yield();
       DBConnection dbconnection = null;
       try {
       dbconnection = // creates db connection
       String query = sqllist.get(i);
       /* uses DB connection pool and exectes query and store result
        * someting like as shown below 
       */ And there is no issue with query or dbconnection
       DBResult DBResult= dbconnection.retrieve(sql);
       // proces returnes result and store in list.
       list.add(DBResult);
       }
       catch (SQLException e)  {
       // log sql exception
       }
       
    }  
    
    return list;
    
}
       
              ```

【问题讨论】:

  • 是的 @Abra 它正在实现 Callable 我刚刚编写了一种伪代码来给出代码流的整体表示。
  • 好的,确定@Abra 已在描述中将其添加为伪代码。我对代码的主要怀疑是“Thread.yield();”所以只需添加与其相关的代码而不是添加完整的类
  • @tevemadar 谢谢你的参考非常有价值。

标签: java multithreading


【解决方案1】:

所以我怀疑 Thread.yield() 是否会导致这种情况。由于所有报告调用此代码流具有相同的线程优先级。

简短的回答是否定的。你走错了方向。如果您真的坚持认为Thread.yield() 是问题所在,请参阅下文。

在查看您的伪代码时,我真的对以下内容感到困惑,这可能就是它在您的帖子中的表示方式。如果代码真的如下所示,则存在错误。我用我的想法注释了每一行:

Callable<ReportDownload>  = new Download(request, requestid);
// the only time you need `FutureTask` is if you want a `Runnable` to
// return something.  Wrapping a Callable in a `FutureTask` seems like a mistake
FutureTask<ReportDownload> futureTask = new FutureTask<>(download);
// why do this at all?  Why not call in the current thread?
ExecutorService executorService = Executors.newFixedThreadPool(1);
// I just about never use execute because there is no way to catch exceptions
executorService.execute(futureTask);
// if your job throws this will hang, prolly the source of your bug
downloadResponse = futureTask.get();
executorService.shutdown();

我认为你在这里使用FutureTask 是有缺陷的。您只需将您的Callable 提交给executorService,然后使用返回的Future。我几乎从不打电话给ExecutorService.execute(),尤其是因为如果您的查询抛出异常,您将看不到它。我敢打赌那是你的问题。如果您的任务引发异常(可能是超时异常),那么 FutureTask.get() 调用将永远不会返回。

// maybe this should be newCachedThreadPool and run all queries?
ExecutorService executorService = Executors.newFixedThreadPool(1);
Callable<ReportDownload> download = new Download(request, requestid);
Future<ReportDownload> future = executorService.submit(download);
// get() waits for the job to complete, if you are submitting multiple queries
// then put the futures in a list and call get on them after you call shutdown()
ReportDownload downloadResponse = future.get();
executorService.shutdown();

另外,为什么不在调用线程中调用它呢?为什么需要线程池?如果您尝试分叉多个查询,那么您应该使用一个线程池并向其提交一堆 Download 对象,但我只看到这里创建了一个线程。

是的,以下代码挂起。我怀疑您的超长时间运行的查询引发了异常,导致您的程序挂起。

// you should not use FutureTask to wrap a Callable
FutureTask<Void> futureTask = new FutureTask<Void>(new Callable<Void>() {
    public Void call() throws Exception {
        throw new Exception();
    }
});
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(futureTask);
// bug: this hangs here because the task never completes
futureTask.get();
executorService.shutdown();

如果你真的认为这与产量有关,请查看Thread.yield() javadocs mention

向调度程序提示当前线程愿意让步 它当前使用的处理器。调度器可以随意忽略这个 暗示... 很少使用这种方法。它可能对调试或测试有用,它可能有助于重现由于竞争条件导致的错误。

yield 的作用很大程度上取决于底层的本机线程实现和操作系统,但我怀疑在这种情况下它的作用很小。我怀疑从您的程序中删除它不会改变您看到的挂起频率。

【讨论】:

    猜你喜欢
    • 2016-06-02
    • 2014-06-20
    • 1970-01-01
    • 2021-10-14
    • 2011-06-15
    • 2014-09-23
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多