Блокировка потоков Java

164
30 декабря 2019, 01:40

Задание: Выполнить приложение командной строки с двумя параметрами, один из которых – количество потоков, другой параметр – количество выводимых строк. Нужно так синхронизовать потоки, чтобы имена потоков выводились поочередно- в каждой строке. Под синхронизацией подразумевается использование конструкции synchronized и методов wait, notify. Не использовать в этом задании флаги для синхронизации потоков, а только методы wait и notify. Также не использовать любые задержки для потоков после начала их работы в виде методов sleep, yield или wait c параметром. Имя первого потока должно всегда выводится в новой строке.

Пример вывода для приложения с параметрами 3 и 2:

Thread-0 Thread-1 Thread-2

Thread-0 Thread-1 Thread-2

Мой код:

class Task implements Runnable {
private Task prevTask;
private int number;
static private int numberOfLines;
static private int numberOfTasks;
static private boolean isBegin;
Task(int number) {
    this.number = number;
}
void setPrevTask(Task prevTask) {
    this.prevTask = prevTask;
}
static void setStaticFields(int numberOfLines, int numberOfTasks) {
    Task.numberOfLines = numberOfLines;
    Task.numberOfTasks = numberOfTasks;
    Task.isBegin = true;
}
@Override
public void run() {
    for (int i = 0; i < numberOfLines; i++) {
        if (isBegin && number == 0) {
            isBegin = false;
        } else {
            synchronized (prevTask) {
                try {
                    prevTask.wait();
                } catch (InterruptedException ie) {
                    System.err.println("Thread " + Thread.currentThread().getName() + " was interrupted");
                }
            }
        }
        synchronized (this) {
            System.out.print(Thread.currentThread().getName() + " ");
            if (number == numberOfTasks - 1) {
                System.out.println();
            }
            this.notify();
        }
    }
}

public class Lab4 {
public static void main(String[] args) {
    try {
        if (args.length != 2) {
            throw new IllegalArgumentException("Illegal number of arguments");
        }
        int numberOfTasks = Integer.parseInt(args[0]);
        int numberOfLines = Integer.parseInt(args[1]);
        Task.setStaticFields(numberOfLines, numberOfTasks);
        Task[] tasks = new Task[numberOfTasks];
        for (int i = 0; i < numberOfTasks; i++) {
            tasks[i] = new Task(i);
        }
        for (int i = 1; i < numberOfTasks + 1; i++) {
            int index = (i + numberOfTasks) % numberOfTasks;
            tasks[index].setPrevTask(tasks[(numberOfTasks + i - 1) % numberOfTasks]);
            new Thread(tasks[index], "Thread-" + index).start();
        }
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
}

Здесь происходит блокировка потоков. Я правильно понимаю, что когда-то notify вызывается раньше, чем соответствующий wait или проблема в другом? Как можно это не допустить или исправить?

Answer 1

Мне кажется чтобы совсем голову не сломать здесь надо применить очередь сообщений. Вспомним Алана Кея и его знаменитые фразы:

Я считал объекты чем-то вроде биологических клеток, и/или отдельных компьютеров в сети, которые могут общаться только через сообщения.

Большая идея это «сообщения»

public class WThread2 extends Thread {
    private static int numberOfLines;
    private static int numberOfThreads;
    private static final Queue<Message> messageQueue = new LinkedList<>();
    public static synchronized void addMessage(Message msg){
        messageQueue.add(msg);
    }
    private static synchronized Message getMessage(){
        return messageQueue.peek();
    }
    private static synchronized Message remove(){
        return messageQueue.poll();
    }
    public static void main(String[] args) {
        //Читаем входные параметры
        if (args.length != 2) {
            throw new IllegalArgumentException("Illegal number of arguments");
        }
        numberOfThreads = Integer.parseInt(args[0]);
        numberOfLines = Integer.parseInt(args[1]);
        //Создаем и запускаем потоки
        Worker[] workers;
        workers = new Worker[numberOfThreads];
        for (int i = 0; i < numberOfThreads; i++){
            workers[i] = new Worker();
            workers[i].start();
        }
        //Создаем очередь сообщений
        for(int i = 0; i < numberOfLines; i++){
            for(int j = 0; j < numberOfThreads; j++) {
                addMessage(new Message(Thread.currentThread(), workers[j], workers[j].getName()));
            }
            addMessage(new Message(Thread.currentThread(), workers[0], "\n"));
        }
        synchronized (Worker.class) {
            Worker.class.notifyAll();
        }
        while (!messageQueue.isEmpty()){
            synchronized (Worker.class) {
                try {
                    Worker.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static class Worker extends Thread{
        public Worker(){
            setDaemon(true);
        }
        @Override
        public void run() {
            super.run();
            synchronized (Worker.class) {
                while(true) {
                    try {
                        Worker.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message msg = getMessage();
                    if (msg!=null && msg.from.getName().equals("main") && msg.to == this) {
                        remove();
                        System.out.print(msg.txt());
                    }
                    Worker.class.notifyAll();
                }
            }
        }
    }
    public static class Message{
        public final Thread from;
        public final Thread to;
        private final String txt;
        public Message(final Thread from, final Thread to, final String txt){
            this.from = from;
            this.to = to;
            this.txt = txt;
        }
        public String txt(){
            return txt + " ";
        }
    }
}
READ ALSO
Поверка метода на исключения(Exceptions)

Поверка метода на исключения(Exceptions)

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

144
Высота дочернего блока

Высота дочернего блока

Как в css сделать так, чтобы высота дочернего блока занимала всю высоту родительского? Высота родительского блока при этом не определена

170
:hover + :not - как оптимально реализовать?

:hover + :not - как оптимально реализовать?

Столкнулась со следующей проблемой, которую хотелось бы решить лаконичнее в одну строкуНеобходимо чтобы при наведении на пункты меню появлялось...

153