Обновить
3
0
srez@srez

Пользователь

Отправить сообщение
Да, сам влетал в это. Прямое следствие негибких явовских генериков. Вот и пришлось такой костыль прикручивать. Таже абсолютно тема, что и якобы нативные методы в JDK, которые просто на уровне JVM подменяются на платформозависимые реализации, а никакого JNI там нет.
Возможно, на редкость удачно решена эта проблема.
Странно, что вы не сравнили аппарат с note 3, он намного ближе и по цене, с учетом акции мегафон, тоже. И также абсолютно прорывный аппарат.
Там не по времени обычно, а первые 3-5-10 тысяч километров идет режим обкатки и двигатель несколько душится софтом. Ссылок не припомню, но иногда это инженеры транслируют журналистам и они это упоминают в обзорах.
Да, мой «телефон» и вовсе 7.7". Очень нравится такой формат, жалко выбор ограничен.
А что их вынудило поменять поведения Стринга? Откровенный даунгрейд, на первый взгляд.
Компилятору абсолютно пофигу сейф ваш код или нет. Дать ему понять, с-но никак.
Заранее понять очень просто, если можно доказать, что всегда будет ребро happens-before, значить трейд сейф, иначе не трейд сейф. Тут на момент вызова лямбды не будет таких ребер. Можно переписать например так:
ArrayBlockingQueue result = new ArrayBlockingQueue(1);
callsomething(() => {… result.put(x);});
Это, кстати, Thread Unsafe код и, соответственно, гонка, даже если callSomething корректно передаст вашу Лямбду в другой поток.
Я, если честно, вообще полагаю, что зря в Яве локальные переменные не являются final по умолчанию. Так что ничего страшного, их вообще крайне редко приходится менять.
Для баланса комментариев напишу, что лично мне лямбд больше всего в современной Яве не хватает. И, скажу более, пожалуй единственное, что ей не действительно не хватает и что реально важно.
Не совсем. Более цивилизованный способ это както донести этой библиотеке, что мы не справляемся. Както ее притормозить. Обычно для этого просто подвешивают листенер, а дальше часто этой библиотеке проще както дропнутые данные восстановить. Скажем перезапросить из сети или еще как. Но вот есть и обратка такая, в виде шанса на дедлок.

Причем, беда в том, что тут ничего и не придумать лучше. Я вот дедлоки не люблю, поэтому листенеры неизбежно могут вызывать одновременно, переставленные итп. Ну либо дизайнить эти локи очень аккурартно, чтобы гарантировать, что их можно получить лишь предсказуемыми трассами и снаружи никто до них доступится не сможет. Это редко удается. Да и еще реже удается поработать с чужим кодом настолько продуманным.

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

Ну и разумен вариант, когда каждый слой приложения имеет 3 звена. Для инпута, для аутпута и собсна бизнесс логики. И все три звена обязаны иметь независимые потоки. Не снимает проблему дедлоков (или как вы верно написали дропов) полностью, но значительно снижает такие риски. Но и цену имеет значительную.
С очередью, как я уже дважды писал есть проблема с неконтролируемым ее ростом и ограниченной памятью. А притормозить листенер, если очередь полна чревато как раз таки дедлоком по вышеописанной схеме. Сделать, как я понимаю, тут принципиально ничего нельзя в общем случае.
Не скажу за других, но в Яве все локи такие, они довольно сложные и алгоритмические. Поэтому тут нет существенной разницы со «своим» алгоритмом. Наружу он точно также может выглядеть базовым перимитивом, а внутри иметь очень неочевидную логику. Тот же synchronized имеет очень много ньюансов и режимов работы, которые еще и конфигурируются на уровне JVM.
Дальше, есть еще одна проблема. Скажем, нас вызывает некая библиотека, чужой код, как коллбек. Причем, коллбек вызывает под своим локом. А надо вызвать эту библиотеку с неким ответом, причем этот вызов идет под тем же самым локом. Причем, поток в котором вызывается коллбек не наш, а чужой и его надо побыстрее освободить, то есть библиотеку вызывать должен другой (наш) поток. Но, также требуется, чтобы наши вызовы с ответом должны идти в томже самом порядке, что и их нашего коллбека. И все это должно иметь фиксированные ограничения по памяти, чтобы не схлопотать под нагрузкой ООМ.

Я вот просто не знаю решения этой задачи в принципе. Всегда приходится чемто пожертвовать, либо остается некий шанс на дедлок, либо всегда можно представить сценарий, когда любые (конечные) требования по памяти будут нарушены.
Ну это не совсем так. Все высокоуровневые примитивы в конечном итоге упирается в Unsafe.park().
Дальше, кроме директивы synchronized есть еще wait и Thread.sleep. Дальше, локи между потоками могут подниматься в нативном коде и это довольно частая ситуация. Кроме того, есть еще спин-лок, когда живой тред крутит касы, пока не получится. То есть даже задиагностировать на живой системе по тред дампу ситуацию дедлока довольно сложно. Так же, всегда можно придумать высокоуровневый примитив, который будет подвисать совершенно неожиданным образом. Например, поток который будет периодически просыпаться, но проверять какие-то условия и снова засыпать, технически это будет live-lock, но по сути дедлок, ибо потоки будут почти все время спать. Например, некая задача просто лежит в очереди в тред пул, но изза нечестной очереди, она никогда не будет этим тредпулом обработана. Формально эта задача вообще не имеет никаких технчиеских блокировок, а на деле она может создать одно из ребер дедлока. Скажем, если она состоит из нескольких частей и одна из частей возьмет лок. Это я к тому, что даже задача диагностики реального дед лока на живой системе крайне нетривиальна.
В Яве все сильно сложнее, примитивов синхронизации очень много и некоторые довольно навороченные.
Мне приходится много работать с многопточным кодом из явы и конечно неприятностей избежать этими 2мя простыми правилами не выходит.
Во первых зависимости не обязаны быть мутексами, это могут быть абсолютно разнообразные межнитевые взаимодействия, скажем спинлоки, может просто волатильный флаг стоять как такой барьер.
Дальше, даже один поток может сам себя залочить, хотя формально это и не дедлок. Скажем если тред разбирающий очередь попытается блокирующе записать в нее же, а она полна.
Дальше, есть проблемы с сторонним кодом или со своим же кодом, но других уровней. Скажем нам надо вызвать некий листенер несколько раз не допустив реордеринга. Для этого обычно все вызовы листенера оборачивают в локи. Но этот листенер ничего не знает об этом и могут быть сюрпризы. Скажем если на событие из сети мы вызывваем листенер, а он посылает нам назад команду послать чтото в сеть. Вот тут часто влетаешь в то, что листенер был вызван под тем же локом, который требуется для того, чтобы чтото послать. А то сложно гарантировать порядок транспортной библиотеке. Ну вот это тоже готовый дедлок.

Сильно упрощает ситуацию с дедлокам паттерн с перекидыванием команд в сингл тред. Шанс словить бяк резко падает, на практике. Все тяжелые операции можно кидать в тредпулы через теже очереди. Если аккуратно прописать тред модель, резко упрощает разработку.
Также хорошо работает правило, никогда не вызывать «чужой» код под своими локами. Реордеринг тут увы придется решать скажем сиквенс намберами.Опять же, вшнешние либы не перепишешь.
Так же есть финт, с превращением дедлоков в лив локи. То есть по таймауту из ожидания лока вываливаться, ресетить транзакцию и с небольшой паузой попытаться еще раз. Тут дедлок превращается в приличную потерю по перформансу, так что стоит мониторить кол-во таких вот откатов и следить, что их немного. Но обычно это всеже лучше.

Тут еще такой момент, можно при вызове некоего кода, которые требует лок2 из кода который требует лок1 можно делать в другом потоке. Скажем, отдать в некий тред команду на исполнение. Но тут это неизбежно отсроченные вызов и легко может привести к ситуации, когда вместо дедлока мы получаем OOME, если же сделать очередь конечной то в момент ее заполненности у нас 2 варианта. Либо выбросить задачу нафиг с ошибкой в лог, либо ждать. Второе очевидно опять же может привести к дедлоку.

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

В общем, можно про дедлоки долго писать, тема богатая.
Намного чаще встречается задачка, когда нельзя в момент провисаний по загрузке нельзя просто схлопывать по ключу. А когда нужно иногда схлопывать, а иногда чтото более умное делать. Вот на эту тему написал я недавне штуку под названием ActiveThrottlingQueue. Работает по принципу очереди событий, когда парсинг событий делится на 2 части, быстрая загрузка событий в локальный структуры и мапы, а затем груповая работа по обработке изменений.
Этот минерал волк хак на самом деле сильно позднее привел к появлению интересного хака в брудворе. Навскидку видео не нашел, видимо починили. Если дроном сделать газилку, а потом через шифт указать любую точку на карте, и подгадать что в момент подхода рабочего денег на газилку бы не оставалось, то рабочий идет в следующую точку поверх всех препятствий. Дрон как бы скользит над картой и может попасть на изолированный остров например. Аналогичная была бага с арконом и темпаларами. На удивление, не могу найти ни видео ни даже детального описания в гугле.

Информация

В рейтинге
6 360-й
Зарегистрирован
Активность