Многопоточная печать от ArrayList - PullRequest
0 голосов
/ 13 января 2019

Я пытаюсь, чтобы регистратор распечатывал все сообщения журнала из приложения на консоль и в будущем - внешний файл. Это следует делать, когда я запускаю функцию: «dumpToConsole». Это должно быть сделано многопоточным через 3 потока, которые все 3 обращаются к CopyOnWriteArrayList. Проблема в том, что вывод не в порядке, а втрое больше, чем должен быть. Вместо 3 сообщений я получаю 9. Мне нужно 3 нити для всей отдельной печати 1, например, вместо каждой нити, печатающей 3.

См. Ниже мою фактическую реализацию этого.

Моя тема:

public class LoggingThread extends Thread {
private boolean isStopped = false;
private CopyOnWriteArrayList<LogMessage> messages;

public LoggingThread(CopyOnWriteArrayList messages) {
    this.messages = messages;
}

@Override
public void run() {
    for (int i = 0; i < messages.size(); i++) {
        writeMessageToConsole(messages.get(i).getMessageText(), messages.get(i).getLogLevel());
    }
}

private synchronized void writeMessageToConsole(String message, LogLevel logLevel) {
    System.out.println(message + " (" + logLevel + ")");
}
}

Мой логгер:

public class Logger implements ILogger {
private static ILogger instance = null;
private CopyOnWriteArrayList<LogMessage> messages = new CopyOnWriteArrayList<LogMessage>();
private LoggingThread thread1;
private LoggingThread thread2;
private LoggingThread thread3;

public static ILogger getInstance() {
    if (instance == null) {
        instance = new Logger();
    }

    return instance;
}

public CopyOnWriteArrayList<LogMessage> getMessages() {
    return messages;
}

public void log(Exception ex) {
    log(ex.getMessage(), LogLevel.FATAL);
}

public void log(String message, LogLevel logLevel) {
    messages.add(new LogMessage(message, logLevel));
}

public LogMessage getLastLog() {
    if(!messages.isEmpty()) {
        return messages.get(messages.size() -1);
    }

    else {
        return new LogMessage("", LogLevel.DEBUG);
    }
}

public void dumpToConsole() {
    log("TEST1", LogLevel.FATAL);
    log("TEST2", LogLevel.DEBUG);
    log("TEST3", LogLevel.FATAL);

    thread1 = new LoggingThread(this.messages);
    thread2 = new LoggingThread(this.messages);
    thread3 = new LoggingThread(this.messages);

    thread1.start();
    thread2.start();
    thread3.start();

    try {
        thread1.join();
        thread2.join();
        thread3.join();
    }

    catch (InterruptedException e) {
        log(e.getMessage(), LogLevel.FATAL);
    }

    thread1.interrupt();
    thread2.interrupt();
    thread3.interrupt();
}
}

И мой класс сообщений:

public class LogMessage {
private String messageText;
private LogLevel logLevel;

public LogMessage(String messageText, LogLevel logLevel) {
    this.messageText = messageText;
    this.logLevel = logLevel;
}

public LogLevel getLogLevel() {
    return logLevel;
}

public String getMessageText() {
    return messageText;
}
}

И результат:

TEST1 (FATAL)
TEST2 (DEBUG)
TEST3 (FATAL)
TEST1 (FATAL)
TEST1 (FATAL)
TEST2 (DEBUG)
TEST3 (FATAL)
TEST2 (DEBUG)
TEST3 (FATAL)

Ответы [ 2 ]

0 голосов
/ 13 января 2019

Отдельный отказ от ответа: забудьте об использовании 3 потоков здесь.

Проблема в том, что вывод не в порядке, а втрое больше, чем должен быть. Вместо 3 сообщений я получаю 9.

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

Итак, прежде всего: как только вы говорите «многопоточность» и говорите о содержимом списка печати, все ставки на заказ отменяются. Конечно, каждый поток будет печатать сообщения в правильном порядке, но вы можете no контролировать, будет ли T1 печатать сначала все сообщения, или только одно, затем 3 из T2, что угодно. Суть «несинхронизированных» потоков заключается в том, что: неопределенный порядок.

Тогда: даже когда вы добавляете необходимые шаги, чтобы как-то «синхронизировать» ваши потоки, вы не получаете ничего от этого.

В памяти есть один список. Существует один выходной файл (или консоль). Использование нескольких потоков для извлечения значений и помещения их в ваш файл ничего не ускоряет! Это только создает накладные расходы, и, как видно: это создает необходимость в большом количестве «синхронизирующего» кода.

0 голосов
/ 13 января 2019

Каждый поток должен получить свою задачу для запуска. Вы реализовали run (), чтобы поток просматривал все сообщения, а не пропускал в каждый поток большую часть сообщений.

Дайте каждому потоку индексы, которые он должен записать в консоль. Предполагая, что вы хотите создать 3 темы и у вас есть 12 сообщений, тогда: Поток 1 будет печатать от 0 до 3, Поток 2 напечатает 4 до 7, Нить 3 напечатает от 8 до 11

public class LoggingThread extends Thread {
private boolean isStopped = false;
private CopyOnWriteArrayList<LogMessage> messages;
private int start,end;

public LoggingThread(CopyOnWriteArrayList messages, int start, int end) {
    this.messages = messages;
    this.start=start;
    this.end=end;
}

@Override
public void run() {
    for (int i = start; i < messages.size() && i<end; i++) {
        writeMessageToConsole(messages.get(i).getMessageText(), messages.get(i).getLogLevel());
    }
}

private synchronized void writeMessageToConsole(String message, LogLevel logLevel) {
    System.out.println(message + " (" + logLevel + ")");
}
}

И создайте свои логи:

thread1 = new LoggingThread(this.messages, 0, this.messages.length()/3);
thread2 = new LoggingThread(this.messages, this.messages/3, 2*(this.messages.length()/3));
thread3 = new LoggingThread(this.messages, 2*(this.messages.length()/3), this.messages.length());

Каждая ветка получила треть сообщений для печати. ​​

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

Однако вы можете подождать окончания каждого потока, прежде чем начинать следующий. это сохранит логи в порядке:

   thread1.start();
   thread1.join();
   thread2.start();
   thread2.join();
   thread3.start();
   thread3.join();

Но тогда нет смысла использовать три потока.

...