Странный тупик (?) - PullRequest
       27

Странный тупик (?)

1 голос
/ 07 ноября 2010

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

private synchronized boolean identifiedLinksHasKey(String linkKey){
        return Parser.identifiedLinks.containsKey(linkKey);
}


private synchronized void putToIdentifiedLinks(String key, TreeSet<String> aset){     
        Parser.identifiedLinks.put(key,aset);
}

Однако в какой-то момент программа зависает (чего не происходит, когда я запускаю ее с одним потоком).Для отладки моего приложения я использовал jstack после его зависания, что дало мне следующий дамп потока:

"Thread-2" prio = 6 tid = 0x0000000006b09800 nid = 0x78fc runnable [0x00000000083ef000]
java.lang.Thread.State: RUNNABLE на java.util.HashMap.put (неизвестный источник) на bgp.parser.Entry. putToIdentifiedLinks (Entry.java:297) - заблокировано <0x00000000853f2020> (bgp.parser.Entry) в bgp.parser.Entry.parseTxtFile (Entry.java:141) в bgp.parser.Entry.run (Entry.java:31)

"Thread-1"prio = 6 tid = 0x0000000006b52800 nid = 0x9390 runnable [0x00000000082ef000]
java.lang.Thread.State: RUNNABLE в java.util.HashMap.getEntry (неизвестный источник) в java.util.HashMap.containsat bgp.parser.Entry. idLinksHasKey (Entry.java:281) - заблокирован <0x00000000853f00e0> (bgp.parser.Entry) в bgp.parser.Entry.parseTxtFile (Entry.java:1bgp.parser.Entry.run (Entry.java:31)

Появляется, что два потока получают доступ одновременноЭто две синхронизированные функции, что противоречит смыслу синхронизации.Та же самая ситуация случается, даже если я использую объектные блокировки.Хотя состояние потоков не ЗАБЛОКИРОВАНО, а ВЫПОЛНЕНО, они ведут себя как заблокированные, возможно, потому, что они одновременно обращаются к одной и той же хэш-карте.

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

Ответы [ 5 ]

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

сравните эти два:

bgp.parser.Entry.putToIdentifiedLinks(Entry.java:297) - locked <<i>0x00000000853f2020</i>>
bgp.parser.Entry.identifiedLinksHasKey(Entry.java:281) - locked <<i>0x00000000853f00e0</i>>

Они держат разные замки.Ключевое слово synchronized блокирует объект экземпляр .(т.е. если вы создадите два объекта Object a=new Object(); Object b=new Object();, блокировка входа a не повлияет на b)

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

Ключевое слово «synchronized» блокируется на уровне объекта. То есть: никакие два синхронизированных метода не могут быть запущены одновременно в пределах одного объекта .

Возможно ли, что есть два разных объекта, которые вызываются из двух отдельных потоков?

редактирование: Повторно посещая трассировку стека, я становлюсь все более уверенным, что это действительно так. Измените код следующим образом.

private boolean identifiedLinksHasKey(String linkKey){
       synchronized(Parser) {
            return Parser.identifiedLinks.containsKey(linkKey);
        }
}

private void putToIdentifiedLinks(String key, TreeSet<String> aset){     
    synchronized(Parser) {    
        Parser.identifiedLinks.put(key,aset);
    }
}

Я сам не пробовал этот код, и я не уверен на 100%, возможно ли использовать класс (Parser) вместо объекта для блокировки. Если это не работает, просто выберите любой (один) объект, который доступен из обоих потоков / экземпляров.

1 голос
/ 07 ноября 2010

Я подозреваю, что методы identifiedLinksHasKey() и putToIdentifiedLinks() выполняются двумя различными экземплярами класса bgp.parser.Entry, и в этом случае synchronized не будет работать.

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

Может быть, вы включили файлы в качестве объектов с гарантированной уникальной идентичностью? Если вы полагаетесь на это, скажем, вы ожидаете, что объект, представляющий файл, гарантированно будет глобально уникальным в памяти. Это будет работать до тех пор, пока уникальность не будет нарушена.

Нарушение уникальности может быть связано с API операционной системы. Windows известна (но редко понимается людьми, например, из Unix), что дескриптор файла созданного файла - это не тот же дескриптор файла, который был найден и открыт через findFirst / findnext.

Рассматривайте файловый API в операционной системе как просто интерфейс API для очень удаленной системы без каких-либо гарантий причин и следствий. Если вы создаете файл, это не значит, что вы можете немедленно найти его, если вы удаляете файл, это может означать, что вы, возможно, все равно найдете его впоследствии. и т.д.

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

Если Parser является одноэлементным классом или статическим членом класса Entry, то синхронизация методов не будет работать, поскольку она защищает только переменные-члены объекта Entry.Статические члены не будут защищены этой схемой.Лучше всего, возможно, сделать член идентифицированного класса LinkLinks классом Parser как ConcurrentHashMap.

...