powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
20 сообщений из 20, страница 1 из 1
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414777
ozzmosis
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте. И извиняйте за многабукф, но по-другому не получилось.

Дано:
1) несколько потоков (в примере ниже их пять);
2) коллекция HashMap<Integer, Long>. Ключ коллекции - монотонно возрастающая послед-сть натуральных чисел. Значение - пофиг какое, хотя бы Thread.currentThread().getId() для каждого из заполняющих потоков.

Задача : затолкать в Map 500 тыс строк, обеспечив параллельное выполнение этой задачи пятью потоками (т.е. дать каждому задание на ввод 100 тыс кортежей; далее это число обозначено LIM4THREAD).

Сваял нечто, пытающееся затолкать эти строки одним из нескольких способов:
1) через AtomicInteger
2) через синхронизированный static-метод
3) нечестным способом, полагающим, что id потока всегда < MAX_INTEGER и вычисляющий для каждого потока и значения переменной-счетчика значение ключа по крестьянской формуле: (int)tid*LIM4THREAD+i
4) ("факультатив") просто плюнул на требование вставки 500 тыс уникальных кортежей и решил отдать эту коллекцию на растерзание пяти потокам, которые выполняли бы вставку так:
Код: java
1.
 m.put( i, tid );

- и это значит, что в коллекции в итоге должно находиться ровно 100 тыс строк (все потоки генерят ключи из одного и того же диапазона, никаких atomicInteger'ов и sync-методов не используем).

Результаты озадачили.
1. Через AtomicInteger:
1.1. Выдает изредка (1 раз в 5-10 запусков) исключение ArrayIndexOutOfBoundsException на строке, добавляющей в коллекцию кортеж, т.е. вот на этом: m.put( newAtomId(), tid );
1.2. Несмотря на то, что число итераций, выполняемых каждым потоком, действительно равно 100 тысячам, общее число строк в коллекции МЕНЬШЕ на 15...20 тысяч(!)
1.3. ВСЕ вызовы m.put(...) возвращали null, что означает: вставляемый ключ был уникален, т.е. дублей не было. И это как-то совсем не вяжется с предыдущим пунктом :(
1.4. Если не давать потоку передых внутри цикла (см ниже: try { Thread.sleep(100); } catch(Exception x) { x.printStackTrace(); }), то изредка возможны какие-то совершенно мёртвые затыки. Код просто НЕ выполняется до конца, всё зависает. И это - самое загадочное :-/

2. Через синхронизированный static-метод
Код: java
1.
2.
3.
4.
5.
  static int syncNewId() {
    synchronized (SyncTest02.class) {
      return ++id;
    }
  }


2.1. Не выявлено (см выше, пункт 1.1)
2.2. Итоговое число строк также меньше требуемого, но уже не на 15-20 тысяч, а "гораздо лучше": всего на 2-3 сотни.
2.3. Так же, как в пункте 1.
2.4. Не выявлено (см выше, пункт 1.4)

3. Через "наивную формулу": ключ = (int)tid*LIM4THREAD+i
3.1. Не выявлено (см выше, пункт 1.1)
3.2. Итоговое число определить невозможно: всё зависает вусмерть, см. 3.4
3.3. Число дублей также определить невозможно по причине заклинивания.
3.4. Мёртвый зависон не просто возможен, а гарантирован на каждом запуске. Иногда - уже на первом потоке, начавшем заполнение, иногда - на последующих.

4. "Факультатив": попытка добавить всеми потоками одни и те же кортежи с ключами от 0 до 99999.
Здесь подпункты 4.1 ... 4.4 точно такие же, как и в предыдущем п. 3 .

Вправьте мозг, плз:
1) как добиться, чтобы в коллекцию таки записалось ровно 500 тыс элементов ?
2) откуда может вылезать ArrayIndexOutOfBoundsException, ведь оно даже в доке для этого метода - HashMap.put() - не оговаривается
3) откуда мёртвые затыки в способах 3 и 4 (да еще на разных стадиях выполнения) ?

Код (перечисленные выше варианты указаны так: [ 1 ], [ 2 ] и т.д.):
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
import java.util.*;
import java.util.concurrent.atomic.*;
import java.text.*;

class SyncTest02 implements Runnable {
  private static DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
  private static int LIM4THREAD = 100000;
  static int id;

  static private AtomicInteger aid = new AtomicInteger();

  static Map<Integer, Long> m = new HashMap<Integer, Long>();

  static int newAtomId() { // for [ 1 ]
    return aid.incrementAndGet();
  }

  static int syncNewId() { // for [ 2 ]
    synchronized (SyncTest02.class) {
      return ++id;
    }
  }
 
  public void run() {
    long tid = Thread.currentThread().getId();
    String tnm = Thread.currentThread().getName();
    int dups = 0;
    System.out.println(df.format( System.currentTimeMillis() )+": tnm="+tnm+" - start. . .");
    for(int i=0, k=1; i < LIM4THREAD; i++, k++) {
      if (1==1 && k==LIM4THREAD/5) {
        k=0;
        System.out.println(df.format( System.currentTimeMillis() )+": tnm="+tnm+" - filling, i="+(i+1));
        try { Thread.sleep(100); } catch(Exception x) { x.printStackTrace(); }
      }

      // dups += m.put( newAtomId(), tid )==null ? 0 : 1; // [ 1 ]
      // dups += m.put( syncNewId(), tid )==null ? 0 : 1; // [ 2 ]
      // hangs! dups += m.put( (int)tid*LIM4THREAD+i, tid )==null ? 0 : 1; // [ 3 ]
      // hangs! m.put( i, tid ); // [ 4 ]
    }
    System.out.println(df.format( System.currentTimeMillis() )+": tnm="+tnm+" - finish: dups="+dups+", m.size()="+m.size());
  }

  //...........
  public static void main(String[] args) {
    SyncTest02 r = new SyncTest02();

    Thread t1 = new Thread( r );
    Thread t2 = new Thread( r );
    Thread t3 = new Thread( r );
    Thread t4 = new Thread( r );
    Thread t5 = new Thread( r );

    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}



Код: plaintext
1.
2.
3.
4.
---------
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b05)
Java HotSpot(TM) Client VM (build 20.6-b01, mixed mode, sharing)
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414780
Лагман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
HashMap не thread safe
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414783
забыл ник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
а чем ConcurrentHashMap не подошел?
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414785
забыл ник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
если задача тестовая и нужно написать потокобезопасную мапу - то использовать обычный хэшмап нельзя, по причине озвученной выше.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414786
ozzmosis
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
а почему отсутствие у него thread-safe свойства отразилось на общем числе элементов ? то есть, почему их меньше, чем нужно, если я ни разу их не удалял, а только добавлял ?
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414791
Лагман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosis,

потому что он сломался
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414792
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosisа почему отсутствие у него thread-safe свойства отразилось на общем числе элементов ? то есть, почему их меньше, чем нужно, если я ни разу их не удалял, а только добавлял ?Поток 1 определяет определяет по хэшкоду индекс сегмента (HashMap.table) для своего ключа Y, и получает, например 5.
Поток 2 определяет определяет по хэшкоду индекс сегмента (HashMap.table) для своего ключа Z, и получает тоже 5.
Поток 1 идет по сегменту, и останавливается на последнем его элементе. Пускай это X.
Поток 2 идет по сегменту, и останавливается на последнем его элементе. Это тоже X.
Поток 1 записывает, что X -> Y
Поток 2 записывает, что X -> Z. Все, Y навсегда потерян.

Классический race condition.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414800
ozzmosis
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
С отсутствием части ключей - ясно, псип.
А мёртвые затыки - они тоже от этого ? Это дедлоки какие-то или что ?
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414827
rfq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosis,

зачем вы вообще используете Map, а не ArrayList, раз ключи у вас не несут информации?
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414837
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rfqзачем вы вообще используете Map, а не ArrayList, раз ключи у вас не несут информации?А Thread.getId() разве несет какую-то полезную информацию? Это тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоков, поэтому комментарии по поводу целесообразности ключей здесь не уместны.

И ArrayList здесь не подойдет никак, так как он тоже не thread-safe.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414840
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosisА мёртвые затыки - они тоже от этого ? Это дедлоки какие-то или что ?Хз, что это, но точно не дедлок, так как внутри HashMap нету работы с критическими секциями. Посмотрите в дебагере, или сделайте дамп тредов через jps/jstack.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414849
ozzmosis
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
cdtyjvЭто тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоковда, именно так.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414854
ozzmosis
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
cdtyjvПосмотрите в дебагере, или сделайте дамп тредов через jps/jstack.Постараюсь, в ближайшем обозримом. Но если там (в дампах) ничего не получится понять, приду снова сюда :-)
Впрочем, главное я понял: надо перечитывать доку ( эту и еще несколько критичных) каждый день. С утра и на ночь.
Ведь видел же этот раздел - Concurrent Map Implementations - а всё равно не запомнил, пока не попробовал пальцАми.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414904
забыл ник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosiscdtyjvПосмотрите в дебагере, или сделайте дамп тредов через jps/jstack.Постараюсь, в ближайшем обозримом. Но если там (в дампах) ничего не получится понять, приду снова сюда :-)
Впрочем, главное я понял: надо перечитывать доку ( эту и еще несколько критичных) каждый день. С утра и на ночь.
Ведь видел же этот раздел - Concurrent Map Implementations - а всё равно не запомнил, пока не попробовал пальцАми.

Вы сорцы ConcurrentHashMap тоже посмотрите - интересная вещь)
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414907
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
забыл никВы сорцы ConcurrentHashMap тоже посмотрите - интересная вещь)Опасно, можно подхватить ДагЛи головного мозга
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
public V get(Object key) {
    Segment<K,V> s; // manually integrate access methods to reduce overhead
    HashEntry<K,V>[] tab;
    int h = hash(key);
    long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
    if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
        (tab = s.table) != null) {
        for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
                 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
            e != null; e = e.next) {
            K k;
            if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                return e.value;
        }
    }
    return null;
}


В 8й Java ее обещают конкретно переделать.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414924
rfq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ozzmosiscdtyjvЭто тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоковда, именно так.
Тогда непонятно, почему не использунтся классическое решение - объект с двумя полями (счетчик значения ключа и HashMap) и синхронизированным нестатическим методом? Что, сразу хочется супер-пупер производительное решение найти? Научитесь сначала находить правильные решения. Неправильные производительные решения нафиг никому не нужны.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38414925
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rfqТогда непонятно, почему не использунтся классическое решение - объект с двумя полями (счетчик значения ключа и HashMap) и синхронизированным нестатическим методом? Что, сразу хочется супер-пупер производительное решение найти? Научитесь сначала находить правильные решения. Неправильные производительные решения нафиг никому не нужны.Ну что вы такое говорите, ей богу!
Я ни разу в жизни не слышал про такое "классическое" решение, это маразм какой-то. Для потокобезопасной работы с коллекциями есть два принципиально разных варианта:
1) Использовать synchronized обертки из класса Collections.
2) Использовать коллекции из пакета java.util.concurrent.
Если автор хочет стать профессионалом своего дела, то он должен знать про оба этих варианта: их сильные и слабые стороны, когда что применять, и т.д.

А инкрементируемый счетчик - это просто способ сгенерировать ключи, не более. Суть проблемы автора не в этом счетчике.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38415139
rfq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cdtyjvЯ ни разу в жизни не слышал про такое "классическое" решение, это маразм какой-то.

Ну это вы погорячились. Не стоит выдавать свое невежество за истину в последней инстанции.
cdtyjvДля потокобезопасной работы с коллекциями есть два принципиально разных варианта:
1) Использовать synchronized обертки из класса Collections.
2) Использовать коллекции из пакета java.util.concurrent.

И в чем принципиальная разница? В обоих случаях доступ синхронизован. И это единственный способ достичь потокобезопасности. Странно что вы не указали третий вариант - сделать синхронизированную обертку самому. Именно это я и предложил, с тем, чтобы включить в синхронизированный блок доступ к ключам.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38415167
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rfqИ в чем принципиальная разница? В обоих случаях доступ синхронизован. И это единственный способ достичь потокобезопасности. Странно что вы не указали третий вариант - сделать синхронизированную обертку самому. Именно это я и предложил, с тем, чтобы включить в синхронизированный блок доступ к ключам.1) Что вы подразумеваете под фразой "доступ синхронизован"? Если вы имеете ввиду синхронизацию доступа к данным, то есть отсутствие data races, то да - он синхронизован. Если же вы имеете ввиду критические секции, то вы неправы. Посмотрите, например, код ConcurrentHashMap, и вы увидите, что тот же put() может быть синхронизован, а может и не быть.
2) Коллекция - это хранилище каких-то значений. Инкремент к коллекции вообще никакого отношения не имеет. Поэтому то, что вы предлагает, это скрещивание ужа с ежом. Автор вообще может взять и разбить 500К ключей на свои 5 потоков по диапазонам 1- 100000, 100001 - 200000, и т.д. И тогда ему вообще никакой синхронизации не надо будет иметь на ключах. Как это впишется в ваше решение? Да никак, ибо уж с ежом. Потому я утверждаю, что идея сращивать генерацию ключей с работой с коллекцией - маразм.
...
Рейтинг: 0 / 0
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
    #38415296
Лагман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rfq,

Принципиальная разница в том что в java.util.concurrent по максимуму используются лок-фри алгоритмы и доступ как раз асинхронный.
...
Рейтинг: 0 / 0
20 сообщений из 20, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]