Nano Hash - криптовалюты, майнинг, программирование

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

В моем основном потоке я создаю и запускаю четыре потока (A,B,C,D), которые печатают буквы и цифры на консоли каждые 500–1000 мс. Например A1,A2,A3 и т.д.

Предполагается, что основной поток приостанавливает случайный Letter поток каждые 100 мс, а затем пробуждает его. Через 2 секунды предполагается убить их всех.

Моя проблема в том, что я не могу приостановить случайный поток Letter, а затем разбудить его, потому что я получаю: IllegalMonitorStateException

Мой класс основного потока:

public class Main extends Thread {
    private boolean alive;
    private ArrayList<Letter> letters;
    private Letter toStop;
    public static Object mutex;

    public Main() {
        letters = new ArrayList<Letter>();
        alive = true;
        mutex = new Object();
    }

        public void run() {
    try {
        Timer timer = new Timer();
        timer.schedule(new StopTask(timer, this), 2 * 1000);
        letters.add(new Letter());
        letters.add(new Letter());
        letters.add(new Letter());
        letters.add(new Letter());
        for (Letter letter : letters) {
            new Thread(letter).start();
        }

        while (alive) {
            synchronized (mutex) {
                toStop = letters.get((int) (Math.random() * letters.size()));
                System.out.println(toStop.getLetter() + " spi");
                mutex.wait();
                Thread.sleep(100);
                mutex.notify();
            }
        for (Letter letter : letters) {
            letter.kill();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

}

    public void kill() {
        alive = false;
    }

}

и мой класс Letter:

class Letter implements Runnable {
    private static int ID;
    private char letter;
    private int counter;
    private boolean alive;

    public Letter() {
        letter = (char) ('A' + ID);
        alive = true;
        ID++;
    }

    @Override
    public void run() {

        try {
            while (alive) {
                System.out.println(letter + "" + counter);
                counter++;
                Thread.sleep((int) (Math.random() * 501 + 500));
            }
            System.out.println("Watek " + letter + " sie zakonczyl");
        } catch (Exception e) {

        }

    }

    public void kill() {
        alive = false;
    }

    public char getLetter() {
        return letter;
    }

} 

StopTask:

import java.util.Timer;
import java.util.TimerTask;

public class StopTask extends TimerTask {
    private Timer timer;
    private Main main;

    public StopTask(Timer timer, Main main) {
        this.timer = timer;
        this.main = main;
    }

    public void run() {
        System.out.println("Time's up!");
        main.kill();
        timer.cancel(); //Not necessary because we call System.exit
    }
}

  • Вы синхронизируете mutex и звоните wait() toStop. Вместо этого вы должны позвонить mutex.wait() 17.05.2015
  • @NitinDandriyal, но я хочу приостановить toStop, пока не вызову уведомление. 17.05.2015
  • @NitinDandriyal Я обновил run в Main, темы никогда не заканчиваются. Я добавил StopTask, так что теперь скопированный код будет компилироваться и запускаться. 17.05.2015
  • Вы должны сначала понять, что для вызова ожидания и уведомления о любом объекте вы должны сначала получить монитор этого объекта, используя ключевое слово synchronized, иначе вы получите это исключение. Перепишите свой код, помня об этом. 17.05.2015
  • в настоящее время ваш mutex виден только основному потоку, остальная часть потока Letter не знает и не заботится об этом, поэтому нет смысла размещать ожидание и уведомлять в основном потоке с текущим кодом 17.05.2015
  • @NitinDandriyal Я знаю об этом, поэтому я опубликовал OP. 17.05.2015

Ответы:


1

Ваш пример кода не работает, потому что вызов wait() выполняется без владения монитором объекта.

Вот объяснение javadoc: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait()

Вот фрагмент из javadoc:

public final void wait() выдает InterruptedException

Заставляет текущий поток ожидать, пока другой поток не вызовет метод notify() или метод notifyAll() для этого объекта. Другими словами, этот метод ведет себя точно так же, как если бы он просто выполнял вызов wait(0). Текущий поток должен владеть монитором этого объекта. Поток освобождает право собственности на этот монитор и ждет, пока другой поток не уведомит потоки, ожидающие на мониторе этого объекта, чтобы проснуться либо с помощью вызова метода notify, либо метода notifyAll. Затем поток ждет, пока он не сможет повторно получить право собственности на монитор, и возобновляет выполнение.

Как и в версии с одним аргументом, возможны прерывания и ложные пробуждения, и этот метод всегда следует использовать в цикле:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

Этот метод должен вызываться только потоком, который является владельцем монитора этого объекта. См. метод notify для описания способов, которыми поток может стать владельцем монитора. Выдает: IllegalMonitorStateException - если текущий поток не является владельцем монитора объекта. InterruptedException — если какой-либо поток прервал текущий поток до или во время ожидания уведомления текущим потоком. Прерванное состояние текущего потока сбрасывается, когда выдается это исключение.

Я бы переделал код, чтобы заставить поток ждать сам, а не ждать извне. Например, используйте некоторые общие объекты, чтобы разделить состояние между потоками. Я бы также использовал исполнителя запланированного потока. Облегчает жизнь: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html

17.05.2015
  • Вы всегда должны добавлять важный фрагмент кода в свои ответы, потому что ссылки могут не работать или могут быть удалены, поэтому фрагменты работают. 17.05.2015
  • Пожалуйста, взгляните на измененный код. Буквы никогда не перестают печататься. 17.05.2015

  • 2

    Это типичная проблема производителя-потребителя, и есть лучшие способы решить ее. Поскольку мы решаем проблему, вы можете сделать это ниже.

    Вы можете избавиться от объекта мьютекса и использовать экземпляр Letter в качестве мьютекса, поэтому фактически у вас есть 4 мьютекса, каждый из которых координируется с Main

    public class Main extends Thread{
        private boolean alive;
        private ArrayList<Letter> letters;
        private Letter toStop;
        //public static Object mutex;
    
        public Main() {
            letters = new ArrayList<Letter>();
            alive = true;
            mutex = new Object();
        }
    
        public void run() {
            try {
                Timer timer = new Timer();
                timer.schedule(new StopTask(timer, this), 2 * 1000);
                letters.add(new Letter());
                letters.add(new Letter());
                letters.add(new Letter());
                letters.add(new Letter());
    
                for (Letter letter : letters) {
                    new Thread(letter).start();
                }
    
                while (alive) {
                    // synchronized (mutex) {
                    toStop = letters.get((int) (Math.random() * letters.size()));
                    synchronized (toStop) {
                        //System.out.println(toStop.getLetter() + " spi");
                        // mutex.wait();
                        //Thread.sleep(100);
                        // mutex.notify();
                        toStop.setToGetLetter(true);
                        toStop.notify();
                    }
                    System.out.println(toStop.getLetter() + " spi");
                    Thread.sleep(100);
                }
                // }
                for (Letter letter : letters) {
                    letter.kill();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        public void kill() {
            alive = false;
        }
    }
    

    И в вашем Letter потоке вы можете использовать this для координации с основным

    public class Letter implements Runnable {
        private static int ID;
        private char letter;
        private int counter;
        private boolean alive;
        private volatile boolean toGetLetter = false;
    
        public boolean isToGetLetter() {
            return toGetLetter;
        }
    
        public void setToGetLetter(boolean toGetLetter) {
            this.toGetLetter = toGetLetter;
        }
    
        public Letter() {
            letter = (char) ('A' + ID);
            alive = true;
            ID++;
        }
    
        @Override
        public void run() {
    
            try {
                while (alive) {
                    synchronized (this) {
                        while (!isToGetLetter()) {
                            this.wait();
                    }
                    System.out.println(letter + "" + counter);
                    counter++;
                    Thread.sleep((int) (Math.random() * 501 + 500));
                    toGetLetter = false;
                    this.notify();
                }
            }
            System.out.println("Watek " + letter + " sie zakonczyl");
        } catch (Exception e) {
    
        }
    }
    
        public void kill() {
            alive = false;
        }
    
        public char getLetter() {
            return letter;
        }
    }
    
    18.05.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..