Простая проблема потока в Java - PullRequest
0 голосов
/ 03 ноября 2010

Работает, за исключением случаев, когда я освобождаю сканер:

public void setCrawlerFree(WebCrawler w)
    {
        synchronized(myFreeCrawlers)
        {
            synchronized(numToGo)
            {
                myFreeCrawlers.add(w);
                myFreeCrawlers.notifyAll();
                numToGo--;
                numToGo.notify();
            }
        }
    }

Когда сканер будет готов, я могу добавить его обратно в список. Я также хочу вычесть 1 из числа вещей, которые мне еще нужно сделать. У меня есть один основной поток, ожидающий, пока numToGo не станет равным 0. Я получаю исключение IllegalMonitorStateException для numToGo.notify (), но, поскольку он находится внутри блока синхронизации, это не значит, что он принадлежит мне?

Ответы [ 5 ]

7 голосов
/ 03 ноября 2010

Попробуйте переписать его на ExecutorService .

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, 
     maximumPoolSize, keepAliveTime, timeUnit, 
     new LinkedBlockingQueue<Runnable>());
executor.submit(new Callable<...>() { ... });

Это значительно упростит ваш код и устранит проблемы с синхронизацией потоков.

3 голосов
/ 03 ноября 2010

Так что я подумал, что мне нужно позвонить и ждать уведомить об объекте, что все темы имеют общее, но это не так исправить тоже.

Да, это так. Но:

public class IllegalMonitorStateException
extends RuntimeException

Брошенный, чтобы указать, что нить имеет пытался ждать на объекте контролировать или уведомлять другие темы ожидание на мониторе объекта без владеющий указанным монитором.

Вам необходимо выполнить синхронизацию с объектом перед вызовом wait() или notify() для него.

0 голосов
/ 04 ноября 2010

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

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

  private final List<Object> trap = new ArrayList<Object>();

    public class BugCatcher {
        public void trapCrawler(Object crawler) {
            synchronized (trap) {
                trap.add(crawler);
                System.out.println("caught bug number " + trap.size() + "!");
                trap.notifyAll();
            }
        }
    }

    public class Hippy {
        public void setCrawlerFree(Object crawler) throws InterruptedException {
            synchronized (trap) {
                trap.wait();
                trap.clear();
                System.out.println("set bugs free! time to hug a tree");
            }
        }
    }

Если BugCatcher может ловить ошибки быстрее, чем хиппи их выпускает, хиппи ждет, пока в ловушке что-то есть, прежде чем пытаться освободить ошибки(следовательно, wait вызов).

Если вы пропустите часть ожидания / уведомления, все будет зависеть только от ключевого слова synchronized, только один поток будет получать доступ к ловушке за раз, и это будет гонка за тем, кто попадет первым (хиппиможет попробовать пустую уже пустую ловушку).

Для координации ожидания и уведомления виртуальная машина будет использовать объектный монитор.Поток получает монитор объекта, когда входит в синхронизированный блок.У объекта есть только один монитор, который действует как взаимоисключающий замок (мьютекс).Если вы попытаетесь и wait или notify без предварительного получения монитора объекта (без выполнения wait или notify в синхронизированном блоке), виртуальная машина не сможет что-то настроить и выдает IllegalMonitorException.Он говорит: «Я не могу этого допустить, потому что, если, например, я подожду, когда я узнаю, что могу прогрессировать, когда кто-то звонит, уведомляет? Что / кого они уведомляют?».Он использует монитор для координации и вынуждает вас захватывать монитор.

Итак, ошибка, которую вы получаете, заключается в том, что numToGo не синхронизирован в другом потоке (как ранее говорил Майкл).

Я не могу понять, зачем вам нужен numToGo, если это производитель / потребитель, вы хотите остановиться после определенного числа?После того, как ловец ошибок ловит 10 ошибок, а хиппи выпускает 10?Не похоже, что это то, что вы пытаетесь сделать (так как они могут иметь не связанные между собой внутренние счетчики), поэтому я не уверен, что вы пытаетесь сделать там.Было бы неплохо обрисовать, что вы пытаетесь сделать, если я ушел совсем по другому!

0 голосов
/ 04 ноября 2010

вы синхронизируете нефинальную переменную-член. Ваша синхронизация (numToGo) синхронизируется с некоторым значением numToGo, а затем вы меняете ссылку: numToGo--. Теперь у вас есть другой экземпляр, для которого вы вызываете notify, отсюда исключение.

0 голосов
/ 04 ноября 2010

Является ли ваше поле numToGo типом примитива, который упаковывается?(int для Integer, long для Long и т. д.).Помните, что эти обертки являются неизменяемыми и заставляют вас иметь разные объекты каждый раз, когда происходит упаковка / распаковка.Всегда рекомендуется использовать конечные объекты, когда требуется синхронизация.

Вместо использования целочисленного и создания собственного объекта для сохранения значения и синхронизации.


class Counter {
    private int value ;
    private final Object lock = new Object() ;

    public ExecutionStatus() { }

    public void increment() {
        synchronized(lock) {
            value ++ ;
        }
    }

    public void decrease() {
        synchronized(lock) {
            value-- ;
        }
    }

    // Read dirty
    public int count() {
        return value ;
    }

    public int safeCount() {
        synchronize(lock) {
            return count() ;
        }
    }
}

Тем не менее, вы можете добавитьстрока private final Object lock = new Object() вашего текущего кода и используйте ее для управления синхронизацией переменной numToGo.

Надеюсь, это поможет.

...