【问题标题】:TimerTask hangsTimerTask 挂起
【发布时间】:2016-02-09 15:37:05
【问题描述】:

我正在尝试通过 Java 中的 TimerTask 功能从 main() 启动一个周期任务,而 run() 任务正在挂起。 Timer 之后的代码是 REPL。代码如下所示:

public static void main( String[] asArguments ){
    java.util.Timer timer = new Timer();
    long durationDelay_ms = 60*60*1000; // 1 hour
    long durationPeriod_ms = 60*60*1000; // 1 hour
    timer.scheduleAtFixedRate(  // reload prices every hour
        new TimerTask() {
            @Override
            public void run() {
                StringBuffer sbError = new StringBuffer();
                if( ! reload( false, sbError ) ){
                    System.out.println( "error reloading prices: " );
                }
                sbError = null; // garbage collect
            }
    }, durationDelay_ms, durationPeriod_ms );

    // REPL
    BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
    while( true ){
        System.out.print( "> " );
        try {
            String s = br.readLine();
            \\ ... code to process command
        } catch( Throwable t ) {
            \\ handle error 
        }
    }
}

public static final boolean reload( ){
    for( int i = 1; i <= 4; i++ ){
        String sURL = "c:\\xyz.com\\" + i; \\ URL I am reading from
        System.out.println( "retrieving data from:\n" + sURL );
        StringBuffer sb = new StringBuffer( 10000 );
        try {
            URL url = new URL( sURL );
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            sb = readStream( con.getInputStream() );
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    System.out.println( sb.toString() );
}

该任务尝试建立多个 URL 连接并打印出每个 URL 的响应。在实践中发生的是“从...检索数据”消息出现一次,然后任务似乎挂起。如果我使用 REPL,通过在 StdIn 输入命令,则 stdout 中会出现“连接超时”错误。

因此,TimerTask 似乎以某种方式与 REPL(在 readline 上阻塞)发生冲突。这是怎么回事?

【问题讨论】:

    标签: java multithreading timer


    【解决方案1】:

    java.util.TimerTask 只有线程来运行任务。如果一项任务继续运行,则其他任务必须等待它完成。 你可以用java.util.concurrent.ScheduledExecutorService替换它,它可以启动更多线程来运行任务...

    我猜你尝试连接的 URL 需要一些“输入”,所以它正在等待额外的数据...... 尝试修改源代码并给它一些数据。例如:

        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setConnectTimeout(5000);//not required
        con.setDoOutput(true);
        con.setDoInput(true);
        OutputStream out = con.getOutputStream();
        out.write("&param1=a&param2=b".getBytes());
        out.close();
        sb = readStream( con.getInputStream() );
    

    【讨论】:

    • 这里没有任务重叠的问题。定时器的周期为 1 小时,因此没有发生任务冲突。正在运行的第一个任务以某种方式被挂起。
    【解决方案2】:

    我最终发现了问题所在。 HttpURLConnection openConnection 方法实现在名为streamHandlerLock 的内部对象上同步,该对象由包括缓冲读取库在内的许多不同的内部Java 库共享。

    这基本上意味着您不能在多个并发线程中使用 java.net 或 java.io 的任何基于流的 IO。

    在我的情况下,REPL 阻塞了BufferedReader.readLine(),因此streamHandlerLock 被冻结。所以,当 openConnection 执行时,它会在这个对象上死锁。

    (我可以在这里谈谈 James Gosling 的编码技巧,他设计并编写了这个糟糕的系统,并在源代码上签名了他的名字,或者我可以谈谈 Sun Oak 团队的彻底失败16 年前 Java 在 2000 年成为主流时重写这个垃圾,但我想我会留给读者想象我可能会说的话。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-29
      • 2013-12-18
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多