5

我在 Solaris 10 上运行 Java 1.5。我的程序是一个独立的 java 程序,使用 java 并发包和 log4j-1.2.12.jar 来记录某些信息。主要逻辑如下

ExecutorService executor = new AppThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(Integer.MAX_VALUE), new AppThreadFactory("BSRT", true), new ThreadPoolExecutor.CallerRunsPolicy());
        CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
        for (final Integer id : taskList) {
            Callable<Integer> c = new Callable<Integer>() {
                public Integer call() throws Exception {
                    int newId = DB operation(id);
                    return newId;
                }
            };
            completionService.submit(c);
        }
        logger.debug("Start retrievie result");
        for (Integer id : taskList) {
            try {
                Future<Integer> future = completionService.poll(1, TimeUnit.SECONDS);               
                Integer taskId=null;
                if (future != null) {
                    logger.debug("future is obtained.");
                    taskId = future.get();
                } else {
                    logger.error("wait too long and get nothing!");
                    break;
                }
                if (taskId != null) {
                    taskIdList.add(taskId);
                }
            } catch (ExecutionException ignore) {
                // log the cause and ignore this aborted task,coninue with
                // next available task.
                logger.warn(ignore.getCause());
            } catch (InterruptedException e) {
                logger.warn("interrupted...");
                // Re-assert the thread’s interrupted status
                Thread.currentThread().interrupt();
            }
        }executor.shutdown();

在执行我的程序期间,有时(并非总是)我会收到此错误...

executor.shutdown(); 

从调用返回后将无法中断 AppThread,super.run(); 因为 woker 已经从内部使用的工作程序集中删除ThreadPoolExecutor,执行程序从那时起没有对 AppThread 的引用。

顺便说一句:日志文件是可访问的并且大小足够大。

log4j:ERROR Failed to flush writer,
java.io.InterruptedIOException
       at java.io.FileOutputStream.writeBytes(Native Method)
       at java.io.FileOutputStream.write(FileOutputStream.java:260)
       at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(StreamEncoder.java:336)
       at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(StreamEncoder.java:404)
       at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(StreamEncoder.java:408)
       at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:152)
       at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:213)
       at org.apache.log4j.helpers.QuietWriter.flush(QuietWriter.java:57)
       at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:315)
       at org.apache.log4j.DailyRollingFileAppender.subAppend(DailyRollingFileAppender.java:358)
       at org.apache.log4j.WriterAppender.append(WriterAppender.java:159)
       at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:230)
       at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:65)
       at org.apache.log4j.Category.callAppenders(Category.java:203)
       at org.apache.log4j.Category.forcedLog(Category.java:388)
       at org.apache.log4j.Category.debug(Category.java:257)
       at AppThread.run( AppThread.java: 33)  

33是行:if (debug) logger.info("Exiting " + getName());

import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;

public class AppThread extends Thread {
    public static final String DEFAULT_NAME = "MyAppThread";
    private static volatile boolean debugLifecycle = false;
    private static final AtomicInteger created = new AtomicInteger();
    private static final AtomicInteger alive = new AtomicInteger();
    private static final Logger logger = Logger.getLogger(AppThread.class);
    private boolean dump = false;

    public AppThread(Runnable r) {
        this(r, DEFAULT_NAME);
    }

    public AppThread(Runnable runnable, String name) {
        super(runnable, name + "-" + created.incrementAndGet());
        logger.debug(name + "'s constructor running");
    }

    public void interrupt() {
        if (!dump) {
            super.interrupt();
        }
        if (dump) {
            logger.debug("interrupt : " + getName() + " <<<");
            Thread.dumpStack();
            logger.debug("interrupt : " + getName() + " >>>");
        }
    }

    public void run() {
        boolean debug = debugLifecycle;
        if (debug)
            logger.info("Created " + getName());
        try {
            alive.incrementAndGet();
            super.run();
            logger.debug("running!");
        } finally {
            alive.decrementAndGet();
            dump = true;
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                logger.debug(e);
            }
            if (debug)
                logger.info("Exiting " + getName());
        }
    }

    public static int getThreadsCreated() {
        return created.get();
    }

    public static int getThreadsAlive() {
        return alive.get();
    }

    public static boolean getDebug() {
        return debugLifecycle;
    }

    public static void setDebug(boolean b) {
        debugLifecycle = b;
    }
}

另一个问题是,为了调试 的原因java.io.InterruptedIOException,我添加了

     try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            logger.debug(e);
        }

runAppThread 方法的 finally 子句中。当InterruptedException 在 finally 子句中被捕获时,interrupt()永远不会调用覆盖方法。那么谁来中断 AppThread 呢?是同一个人原因java.io.InterruptedIOException吗?

4

3 回答 3

9

是的:

shutdownNow 尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。

除了尽最大努力停止处理正在执行的任务之外,没有任何保证。例如,典型的实现将通过 Thread.interrupt() 取消,因此任何未能响应中断的任务可能永远不会终止。

Java 文档

只需使用shutdown()而不是shutdownNow(). 当你强行调用shutdownNow()它时,你应该期待 - JVM 优雅地中断 I/O 并尽可能快地关闭线程。

但是,我会确保日志记录不是您应用程序的瓶颈。只需在程序执行期间进行少量线程转储,然后查看线程写入或等待 I/O 的频率。穷人的简介。

于 2011-09-27T20:53:05.797 回答
2

中断工作线程实际上是框架的一个特性Executor允许工作线程在通过interrupt(). 这是记录在案的行为shutdownNow()

如果您不想要这个,请致电shutdown()- 它不会interrupt()是您的工作线程,Executor它将停止接受新任务。

于 2011-09-27T20:53:56.523 回答
0

我有类似的问题。我的研究到目前为止Thread.interrupt()设置了中断标志。这会导致 Java 堆栈深处的 IO 操作中断。但是 IO 方法通常不会声明为抛出InterruptedException.

而是InterruptedIOException抛出一个并清除线程的中断状态!. 如果您编写了一个期望 (catch)IOException的 Worker,则必须InterruptedIOException单独捕获并调用Thead.currentThread().interrupt()catch 子句。

于 2015-06-11T08:11:02.887 回答