Thread.interrupt () и java.io.InterruptedIOException - PullRequest
1 голос
/ 28 сентября 2011

Я использую Java 1.5 на Solaris 10. Моя программа является автономной 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);
        }

в пункте finally в методе run для AppThread. когда InterruptedException перехватывается в предложении finally, метод переопределения interrupt() никогда не вызывается. так кто прерывает AppThread? тот же парень причина java.io.InterruptedIOException?

Ответы [ 3 ]

7 голосов
/ 28 сентября 2011

Да:

shutdownNow Пытается остановить все активно выполняющиеся задачи , останавливает обработку ожидающих задач и возвращает список задач, ожидающих выполнения.

Нет никаких гарантий, кроме попыток изо всех сил прекратить обработку активно выполняемых задач.Например, типичные реализации будут отменяться через Thread.interrupt () , поэтому любая задача, которая не отвечает на прерывания, может никогда не завершиться.

JavaDoc .

Просто используйте shutdown() вместо shutdownNow().Когда вы принудительно вызываете shutdownNow(), это то, что вы должны ожидать - JVM грациозно прерывает ввод-вывод и максимально быстро завершает поток.

Однако я хотел бы убедиться, что регистрация не является узким местом вваше приложение.Просто сделайте несколько дампов потока во время выполнения вашей программы и посмотрите, как часто потоки пишут или ожидают ввода-вывода.Профилирование бедняков.

1 голос
/ 28 сентября 2011

Прерывание рабочих потоков на самом деле является функцией платформы Executor, позволяющей рабочим потокам корректно завершать работу при получении запроса через interrupt().Это задокументированное поведение для shutdownNow().

Если вы не хотите этого, позвоните shutdown() - он не будет interrupt() ваши рабочие потоки, Executor будетпросто перестань принимать новые задания.

0 голосов
/ 11 июня 2015

У меня похожие проблемы.Мое исследование зашло так далеко, что Thread.interrupt() устанавливает флаг прерывания.Это приводит к прерыванию операции ввода-вывода в глубине стека Java.Но методы ввода-вывода обычно не объявляются для выдачи InterruptedException.

. Вместо этого генерируется InterruptedIOException, и прерванное состояние потока очищается! .Если вы написали Worker, который ожидает (перехватывает) IOException s, вы должны перехватить InterruptedIOException отдельно и вызвать Thead.currentThread().interrupt() в предложении catch.

...