|
|
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
Здравствуйте. И извиняйте за многабукф, но по-другому не получилось. Дано: 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. - и это значит, что в коллекции в итоге должно находиться ровно 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. 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. Код: plaintext 1. 2. 3. 4. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:32:14 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
HashMap не thread safe ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:37:13 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
а чем ConcurrentHashMap не подошел? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:39:20 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
если задача тестовая и нужно написать потокобезопасную мапу - то использовать обычный хэшмап нельзя, по причине озвученной выше. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:40:41 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
а почему отсутствие у него thread-safe свойства отразилось на общем числе элементов ? то есть, почему их меньше, чем нужно, если я ни разу их не удалял, а только добавлял ? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:42:32 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
ozzmosis, потому что он сломался ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:52:18 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
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. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 18:53:31 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
С отсутствием части ключей - ясно, псип. А мёртвые затыки - они тоже от этого ? Это дедлоки какие-то или что ? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 19:06:04 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
ozzmosis, зачем вы вообще используете Map, а не ArrayList, раз ключи у вас не несут информации? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 19:46:19 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
rfqзачем вы вообще используете Map, а не ArrayList, раз ключи у вас не несут информации?А Thread.getId() разве несет какую-то полезную информацию? Это тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоков, поэтому комментарии по поводу целесообразности ключей здесь не уместны. И ArrayList здесь не подойдет никак, так как он тоже не thread-safe. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 20:02:45 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
ozzmosisА мёртвые затыки - они тоже от этого ? Это дедлоки какие-то или что ?Хз, что это, но точно не дедлок, так как внутри HashMap нету работы с критическими секциями. Посмотрите в дебагере, или сделайте дамп тредов через jps/jstack. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 20:07:54 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
cdtyjvЭто тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоковда, именно так. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 20:33:01 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
cdtyjvПосмотрите в дебагере, или сделайте дамп тредов через jps/jstack.Постараюсь, в ближайшем обозримом. Но если там (в дампах) ничего не получится понять, приду снова сюда :-) Впрочем, главное я понял: надо перечитывать доку ( эту и еще несколько критичных) каждый день. С утра и на ночь. Ведь видел же этот раздел - Concurrent Map Implementations - а всё равно не запомнил, пока не попробовал пальцАми. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 20:39:38 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
ozzmosiscdtyjvПосмотрите в дебагере, или сделайте дамп тредов через jps/jstack.Постараюсь, в ближайшем обозримом. Но если там (в дампах) ничего не получится понять, приду снова сюда :-) Впрочем, главное я понял: надо перечитывать доку ( эту и еще несколько критичных) каждый день. С утра и на ночь. Ведь видел же этот раздел - Concurrent Map Implementations - а всё равно не запомнил, пока не попробовал пальцАми. Вы сорцы ConcurrentHashMap тоже посмотрите - интересная вещь) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 22:42:23 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
забыл никВы сорцы ConcurrentHashMap тоже посмотрите - интересная вещь)Опасно, можно подхватить ДагЛи головного мозга Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. В 8й Java ее обещают конкретно переделать. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 22:52:28 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
ozzmosiscdtyjvЭто тестовая задача, автор хочет разобраться, как наполнять коллекцию из нескольких потоковда, именно так. Тогда непонятно, почему не использунтся классическое решение - объект с двумя полями (счетчик значения ключа и HashMap) и синхронизированным нестатическим методом? Что, сразу хочется супер-пупер производительное решение найти? Научитесь сначала находить правильные решения. Неправильные производительные решения нафиг никому не нужны. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 23:31:28 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
rfqТогда непонятно, почему не использунтся классическое решение - объект с двумя полями (счетчик значения ключа и HashMap) и синхронизированным нестатическим методом? Что, сразу хочется супер-пупер производительное решение найти? Научитесь сначала находить правильные решения. Неправильные производительные решения нафиг никому не нужны.Ну что вы такое говорите, ей богу! Я ни разу в жизни не слышал про такое "классическое" решение, это маразм какой-то. Для потокобезопасной работы с коллекциями есть два принципиально разных варианта: 1) Использовать synchronized обертки из класса Collections. 2) Использовать коллекции из пакета java.util.concurrent. Если автор хочет стать профессионалом своего дела, то он должен знать про оба этих варианта: их сильные и слабые стороны, когда что применять, и т.д. А инкрементируемый счетчик - это просто способ сгенерировать ключи, не более. Суть проблемы автора не в этом счетчике. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 02.10.2013, 23:38:22 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
cdtyjvЯ ни разу в жизни не слышал про такое "классическое" решение, это маразм какой-то. Ну это вы погорячились. Не стоит выдавать свое невежество за истину в последней инстанции. cdtyjvДля потокобезопасной работы с коллекциями есть два принципиально разных варианта: 1) Использовать synchronized обертки из класса Collections. 2) Использовать коллекции из пакета java.util.concurrent. И в чем принципиальная разница? В обоих случаях доступ синхронизован. И это единственный способ достичь потокобезопасности. Странно что вы не указали третий вариант - сделать синхронизированную обертку самому. Именно это я и предложил, с тем, чтобы включить в синхронизированный блок доступ к ключам. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 10:39:13 |
|
||
|
Непонятки при заполнении HashMap'a с монотонно возр. int-ключем несколькими потоками
|
|||
|---|---|---|---|
|
#18+
rfqИ в чем принципиальная разница? В обоих случаях доступ синхронизован. И это единственный способ достичь потокобезопасности. Странно что вы не указали третий вариант - сделать синхронизированную обертку самому. Именно это я и предложил, с тем, чтобы включить в синхронизированный блок доступ к ключам.1) Что вы подразумеваете под фразой "доступ синхронизован"? Если вы имеете ввиду синхронизацию доступа к данным, то есть отсутствие data races, то да - он синхронизован. Если же вы имеете ввиду критические секции, то вы неправы. Посмотрите, например, код ConcurrentHashMap, и вы увидите, что тот же put() может быть синхронизован, а может и не быть. 2) Коллекция - это хранилище каких-то значений. Инкремент к коллекции вообще никакого отношения не имеет. Поэтому то, что вы предлагает, это скрещивание ужа с ежом. Автор вообще может взять и разбить 500К ключей на свои 5 потоков по диапазонам 1- 100000, 100001 - 200000, и т.д. И тогда ему вообще никакой синхронизации не надо будет иметь на ключах. Как это впишется в ваше решение? Да никак, ибо уж с ежом. Потому я утверждаю, что идея сращивать генерацию ключей с работой с коллекцией - маразм. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 03.10.2013, 10:59:50 |
|
||
|
|

start [/forum/topic.php?fid=59&fpage=207&tid=2128493]: |
0ms |
get settings: |
8ms |
get forum list: |
15ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
56ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
52ms |
get tp. blocked users: |
1ms |
| others: | 225ms |
| total: | 375ms |

| 0 / 0 |
