Pull to refresh

Comments 50

>У меня Котлин
А у меня Java 8. И так будет еще долго.
Всё это очень похоже про разговоры о Java SE 6 в 2014, когда появилась сама Java SE 8. Вы всегда можете уйти туда где Java SE 11 в проде сейчас.
Зачем мне туда? У меня вполне интересные задачи тут.

Все дело в том, что оракл (выпустив Java 9) поменял свою политику выпуска релизов, и эта новая политика частых релизов многих не устраивает на самом деле. Новые релизы нужны тем, кто делает новые проекты, а тем кто эксплуатирует старые много лет — нужна стабильность и совместимость. А они ее сломали. Это был пожалуй единственный релиз, который сломал например maven и gradle. Хотя уж казалось бы, там-то что можно сломать? Даже Java 5 не вызывала таких проблем при миграции (я в своей практике мигрировал раз пять, а считая JavaEE — еще больше).
— Хотя уж казалось бы, там-то что можно сломать?
Это Jigsaw, класс Unsafe и Reflection, чтобы не получали доступ к тому, что не было запроектировано.

Проект Jigsaw ломает доступ к использованию непубличного API. Поэтому сравнение с Java SE 5 некоректно. Когда получилось проект влить в релиз — тогда и сделали. Так совпало, что всё это попало в Java SE 9, а могли и в 8 уже всё это увидеть при определённой развитии истории.

Стабильность это LTS: Java SE 8, 11, 17. Все другие выпуски проходные и мне непонятно зачем крутить счётчик 2 раза в год. Зря отказались от версионирования как в Ubuntu.
>— Хотя уж казалось бы, там-то что можно сломать?
>Это Jigsaw, класс Unsafe и Reflection, чтобы не получали доступ к тому, что не было >запроектировано.
Не уверен, что gradle и мавену это все нужно. Скорее уж сломали что-то в класслоадерах (и даже примерно понятно, что именно).

>Проект Jigsaw ломает доступ к использованию непубличного API. Поэтому сравнение с Java SE 5 некоректно.
Сравнение тут затем, что даже сложный переход на generics не стоил когда-то таких усилий (ну, это мои впечатления, если что). И судя по ним же, никогда столько народу не оставалось сидеть на старых версиях, как после выхода Java 9. Когда это получилось — уже не так важно. Важно что это решение сломало обратную совместимость, с которой до этого все было сильно получше.

Ну то есть, я почему maven и gradle упомянул — потому что до этого никогда релизы не ломали такие базовые вещи, как системы сборки — идеологически достаточно простые, в общем-то. А тут сумели. А уж если говорить о проектах посложнее…

Вот оно в 2017 вышло — а до сих пор скажем Hadoop не может завершить миграцию как следует. И какой вывод напрашивается? Что на пользователей хадупа при выпуске 9 наплевали. Что ораклу нужно было решить свои проблемы. Ну вот они наплевали и решили — теперь и имеем то что имеем. Какая там доля Java 8 на сегодня, не напомните?
Ваша правда, что появление Обощённого/Шаблонизированного кода в Java SE хоть и было отложено от версии 1.0 в 1995, но решение в Java SE 5.0 было почти без боли. Раздосадованными конечно остались те кто был не доволен, что оно работает только на этапе компиляции, а не во время исполнения.
Видите ли вы применение Project Loom в своем проекте?

Голосование не совсем корректное, так как отсутствует мультивыбор.


В частности, для проекта с Kotlin и Oracle + JDBC имеет смысл протестировать loom. Oracle не планирует уже делать неблокирующий интерфейс, так что только loom и спасет.


Далее, если loom окажется быстрее корутин (а для этого есть все шансы, так как он, например, выделяет меньше памяти, плюс обрабатывает исключения нативно), то вообще имеет смысл уходить от условного ktor client на старый добрый Apache Client, который автоматом станет неблокирующим. Заодно можно получить потоковую десериализацию.

Они оба неблокирующие. Как и ktor.


После loom у них у всех трех будет одна общая проблема: все создают ненужные объекты в куче, т.е. CompletableFuture, Mono, Flux, Flow и так далее. Плюс, ради проброса исключений, у них есть код, который "восстанавливает callstack", который тоже требует ресурсов. Плюс улетают JVM оптимизации вида "выделяем объект на стеке, так как он не возвращается наружу никогда" и море других похожих.


Кстати, Spring Flux тоже будет не особо нужен, так как он выделяет море лишних объектов в куче ради асинхронности. Вот пример такой проблемы (она осталась, просто не видна), вот статья про неё на хабре.


Servlet 4.0 (см. 2.3.3.3) тоже содержит неблокирующие опции, от которых надо будет избавляться, как от ненужных.


Netty весь построен на колбеках, что будет не нужно.


В общем, Loom полезный проект. Однако из-за него, для более эффективной работы, надо будет переписать много чего, чтобы избавиться от текущей модели явной асинхронности и начать использовать неявную. А значит и потребуются правки в коде приложений.

В Erlang изначально green threads, но тем не менее используются акторы. Думаю, что и в Java абстракции над многопоточностью и асинхронность смысла не потеряют, просто будут выжимать меньший процент производительности.

То что вы написали это все правда. Вопрос был в другом зачем уходить на


то вообще имеет смысл уходить от условного ktor client на старый добрый Apache Client

когда описанные асинхронные клиенты удобней в плане работы с АПИ.


Я часто видел стектрейсы гораздо длинней на обыкновенной джаве :)))


Вот пример такой проблемы (она осталась, просто не видна)
Если вы про HTTP Client, Apache Client тоже ненужен, в Java 11+ есть HTTP client в стандартной библиотеке
А чем вся эта кухня лучше обычного:
for (int i=0;i<1000_000;i++)
    tasks.add(()->/* do something */);
for (int i=0;i<tasks.size();i++)
    task.get(i).run();

Ради чего расширяется набор базовых библиотек???
Видимо тем что если внутри .run() будет сетевой вызов или чтение файла — на время ожидания ввода-вывода автоматом переключится на другой task
С чего вы это взяли?

Не знаю, что конкретно сочинили в Java15, но «переключиться на другой таск» означает ровно всё то же самое, что и переключиться на другой поток, а значит нет главного — никаких отличий от старого.

Хотя опять же есть асинхронное IO. Если мне нужно «переключаться на другой таск», то я сам укажу программе где и как я хочу переключиться, просто использовав что-то вроде пакета nio. Соответственно — зачем мне нововведение, где что-то действует неконтролируемым образом?

По сути вопрос о внутреннем устройстве нововведения. Если оно известно — можно что-то обсуждать. Если нет — просто нечем обосновывать ценность нововведения. Мне неизвестно, думал автор статьи пояснит, но он отмалчивается. Значит придётся ждать более вменяемых описаний.
Спасибо, почитал. Не смотря на откровенно быдляцкий язык автора той статьи, понять его вполне можно. Суть в расшивании проблемы stack overflow при кооперативном переключении (включая неявное) на другие псевдо-потоки. Хотя неявное переключение, как пишет автор, реализовали очень ограничено (но от его стиля изложения веет полнейшим трешем, поэтому непонятно, кто здесь виноват — пошлость автора или всё же ограничения со стороны разработчиков JVM).

Но тем не менее всё так же непонятно, с чего автор обсуждаемой здесь статьи (уже не по вашей ссылке) считает, что в подобной реализации возможно какое-то другое вытеснение псевдо-потоков кроме кооперативного. Судя по данным по вашей ссылке никакого другого подхода там быть не может (кроме кооперации и неявного переключения в некоторых избранных местах, в виде нативных вызовов, которые и реализуют переключение). Может автору тоже стоит познакомиться с материалом по ссылке.

А в чём проблема кооперативного вытеснения?

Теряется смысл потока.

Потоки загружают ядра. Если убрать переключение потоков — ядра будут недогружены. Кооперация гарантирует, что переключение во многих случаях работать перестанет из-за множества проблем на стороне писателей в кооперативном стиле.

Но и вам вопрос — а в чём преимущества кооперации потоков?

С чего бы ядра будут недогружены-то?

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

Ну то есть вы легко сможет привести пример полезного использования озвученной технологии?
Например Erlang с его green threads.

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

Понятно. Получается, что все отвечающие примера привести не могут.

Хотя да, общие слова — это наше всё. Так что я обязательно поставлю галку «зачтено» в виртуальном журнале против ваших ников.

Ну и так, напоследок:
for (int i=0;i<1000_000;i++)
    tasks.add(()->/* do something */);
for (int i=0;i<tasks.size();i++)
    task.get(i).run();

И два:
for (int i=0;i<1000_000;i++)
{ /* do something */ }

Что по вашему проще?

Смотрите если в tasks у вас обычные треды вы там с блокирующими операциями просядите. Это понятно?
С виртуальными тредами не просядите, потому что когда до операции ввода/вывода дойтете то виртуальный тред хоть и будет ждать, но реальный не будет простаивать.
Тем самым вы упрощаете код который у вас:


/* do something */

там вы можете в лоб теперь блокирующе читать файлы или сокет… 100 000 раз. и все это без колбэков в коде. У меня нет под рукой сейчас примера с колбэком и без. Влом выдумывать.


а то что вы показали останется тем же.

вы там с блокирующими операциями просядите

Дело в том, что альтернатива, во первых (а это ошибки), неявно, а во вторых, для очень ограниченного количества действий, расшивает указанную вами проблему. Из этого следует, что каждый раз программист будет думать, долго и без гарантий правильности. В итоге будут все те же ошибки, что и с обычными потоками, плюс дополнительно проблемы из-за специфики псевдо-потоков. В сумме = хуже.

Ну и помимо «общечеловеческих ценностей» есть ещё и неблокирующее IO API. И что мешает просто взять и его использовать? Не наступая на грабли с новыми псевдо-потоками. Ведь вы именно на асинхронное IO упираете, доказывая ценность псевдо-потоков. И вот оно, асинхронное IO, и так есть, и давно используется, ну и без приставки псевдо.
неблокирующее IO API. И что мешает просто взять и его использовать?

Используйте. Вам никто не мешает.
Но неблокирующее какраз больше кода, об этом же и пишу вам. Сложнее ментальная модель, сложнее в дебаге.
С новыми тредами вам это не нужно будет во многих случаях. Тоесь вы просто можете забитъ на ассинхронне апи, БЕЗ особого усложнения понятия тред. Вы почему-то пытаетесь доказать что треды будут сложнее. На мой же взгляд разница помойму очень проста, еще один уровень абстракции как в Го с сохранением АПИ.
Может у вас свой специфический контектс и там все так как вы говорите — тогда это частный случай скорее.


ИМХО мне кажется это очень правильное направление и упросит в будушем многое. 16 ядрами сейчас никого не удивишь к примеру. С лекговесными треадами и простым блокирующим кодом их будет проще запользовать — это правильное направление ИМХО.

это правильное направление


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

А то, что сейчас джависты идут по пути, который, скажем, JavaScript комьюнити уже прошло N-лет назад (callback hell -> promises hell -> control flow streams\...), наступая по пути на все те же грабли просто потому что «мы привыкли писать синхронный блокирующий код но это нормально не работает на 32ядерном тред-риппере, давайте закостылим!». Просто смешно.

Если бы это была какая-то революция — еще можно было понять. Сейчас асинхронный конкурентный код — это стандарт для высоконагруженного бэкенда.

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

дайте хоть пример того что не учтено?
П.С. Смотрите на Golang. Я лично не понимаю пока вашей аргументации. Мне кажется вы противоречите себе.

Костыли это как раз JavaScript в бэкенде. callback hell и т.д. Костыли это потому что технологию, которая работает в браузере, где событийно-ориентированность это нормально и естественно, притащили в бэкенд. И если 20 лет назад это дейтсвительно работало лучше, потому что процессор был один. То в современных реалиях это жутко неффективно и тотально проигрывает нормальным тредам на современном 32-х ядерном тредрипере. Потому что треды на таком тредрипере выполняются действительно параллельно.

Но фронтендеры, привыкшие обрабатывать нажатие мышки по кнопочке, не понимают как написать многопоточную программу, вот и лепят свою лапшу из коллбэков в свою node.js.

Скажите, а что вам, настоящим бэкендерам, мешает обрабатывать асинхронные задачи многопоточно?


Kestrel вот почти без усилий держит 10 000 одновременных HTTP соединений, мы в феврале это выяснили. А многопоточный сервер в тех же условиях умирает.

Скажите, а что вам, настоящим бэкендерам, мешает обрабатывать асинхронные задачи многопоточно?


Ну обычно в защиту лапши из коллбэков приводят аргумент мол ненадо синхронизироваться, а если их обрабатывать многопоточно — синхронизироваться все-таки надо, ну и теряется смысл такой системы

Это вы какие-то странные аргументы читали. Смысл асинхронности — в уменьшении требований к ресурсам: не нужно выделять кучу памяти в виде стека для потока, в том числе в режиме ядра.

Но фронтендеры, привыкшие обрабатывать нажатие мышки по кнопочке, не понимают как написать многопоточную программу, вот и лепят свою лапшу из коллбэков в свою node.js.


Звучит как какая-то обида на фронтенд…

callback hell и т.д.


Что значит «и т.д.»? Callback hell — это были первые попытки структурировать асинхронный код. С тех пор мир уже ушел далеко вперед и есть гораздо более эффективные и элегантные решения о чем я и писал в своем комментарии.

То в современных реалиях это жутко неффективно и тотально проигрывает нормальным тредам на современном 32-х ядерном тредрипере. Потому что треды на таком тредрипере выполняются действительно параллельно.


100%. Поэтому я и говорю, что для высоконагруженной программы нужно писать асинхронный + конкурентный код. Это позволит добиться наилучших результатов при использовании ограниченных вычислительных ресурсов
Посмотрите пожалуйста статью What Color is Your Function?

Project Loom как раз пытается хотя бы частично решить именно те проблемы, о которых Вы пишете. Эти проблемы есть во всех языках, поддерживающих синхронные и асинхронные вызовы. И пока совсем идеального решения никто не нашел.

Java — это не про революцию. И это очень хорошо.
Тут у некоторых людей проблемы с переездом с 8-ки из-за добавления микросекунд в Instant.now(), а Вы про какую-то революцию говорите.

Ну и я совсем не понял, почему Вы ставите знак равенства между «асинхронность» и «высокая нагрузка», не поясняя, что имеется в виду под «нагрузка».
Ну и я совсем не понял, почему Вы ставите знак равенства между «асинхронность» и «высокая нагрузка», не поясняя, что имеется в виду под «нагрузка».


Не совсем так. Я пытался сказать, что асинхронный конкурентный код — это самый эффективный способ использования вычислительных ресурсов, а значит — позволяет наиболее эффективно (с точки зрения потребления ресурсов) обрабатывать высокие нагрузки. Под нагрузками я имею ввиду то, что подразумевает термин highload.

Асинхронный == треды не простаивают впустую.
Конкурентный == все ядра CPU используются по максимуму.

а Вы про какую-то революцию говорите


Я про эволюцию. Чтобы подходы, которые имели место быть 10-15-20 лет назад заменялись подходами, которые актуальны сейчас.

Посмотрите пожалуйста статью What Color is Your Function?


Почитал, но что-то полезное оттуда вынести сложно. Как я выше написал — люди уже пережили callback-hell и прочие грабли с промисами. Уже есть адекватные механизмы построения control-flow Вашей асинхронной программы. Статья 2015 года, поэтому неудивительно, что автор не упоминает практически ни одного из них. Думаю, что в то время они были не особо распространены. Сейчас фактически все перечисленное там — не актуально.

Project Loom как раз пытается хотя бы частично решить именно те проблемы, о которых Вы пишете


Еще раз — я двумя руками за грин треды и прочие решения этих проблем. Но очень хотелось бы, чтобы это не было в стиле — «я обновил версию JVM и о чудо мой блокирующий код стал не блокирующим». Потому что это не сработает. По крайней мере в Java-мире.
Но очень хотелось бы, чтобы это не было в стиле — «я обновил версию JVM и о чудо мой блокирующий код стал не блокирующим»

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

Знаком не только с вышеприведенной статьей, но так же с оригинальным выступлением:
https://www.youtube.com/watch?v=J31o0ZMQEnI
Как можно видеть, графики и объяснение как работает развертывание стека были взяты как раз оттуда.


У том, что с Project Loom возможен и forced preemtion Ron Pressler писал мне тут:
https://twitter.com/pressron/status/1262350580820869120


И так же вскользь упоминает об этом в State of Loom:


For this reason, none of the schedulers in the JDK currently employs time-slice-based preemption of virtual threads, but that is not to say it won’t in the future — see Forced Preemption.
Многоуважаемый commenter, Ваша показная интеллигентность, по моему скромному мнению, не достаточно хорошо прикрывает неприкрытое хамство, но все же, предложу Вам обратить внимание на год публикации статьи по ссылке, а затем проследить за изменениями, которые произошли в Project Loom за все прошедшее время. Включая статью Ron Pressler, с которой начал автор текущей статьи.
Асинхронной IO — это уже лапша с коллбэками, почему это не эффективно читайте выше комментарий habr.com/ru/post/503412/#comment_21650048. Я бы ещё добавил что асинхронной IO это абсолютно не читаемый и не поддерживаемый код.
В упомянутом API нет необходимости в коллбэках. Там достаточно уметь опрашивать каналы в цикле.

В целом стремление впихнуть в абстракцию потока миллионы экземпляров точно так же чревато адской неэффективностью. Даже если памяти на псевдо-поток будет тратиться поменьше, дальнейшая обработка никак из штанов выпрыгнуть не сможет, ну и вся затея будет иметь смысл в очень и очень ограниченных нишах. Могу себе представить разве что какой-то самый примитивный роутер, распихивающий потенциально очень большие флуктуации на входе по вот таким недо-потокам, которые в итоге будут служить лишь цели усложнения архитектурного шаблона «очередь» (и памяти потреблять, разумеется, больше, чем оптимизированная очередь в памяти).

Хотя возможно вы можете представить какое-то более разумное применение для сабжа?

Опрашивать каналы в цикле — тоже не самая очевидная вещь, когда всего лишь хотелось бы прочесть файл.

Начать стоит с того, что вышеприведенный код запустится в одном потоке.

UFO just landed and posted this here
Ведь это по факту копирует работу планировщика системы — не перечеркивает ли это все плюсы корутин?

Не перечеркивает. Из достоинств корутин стоит отметить динамический размер стека и более быстрый context switch. Так что иметь forced preemtion — полезная вещь, вот только Go в этом плане всегда ударяется в крайности. Либо preemtion для всех, либо ни для кого.

вот эти все модные корутины похожи на реализацию кооперативной многопоточности.

неа


Virtual threads are preemptive, not cooperative — they do not have an explicit await operation at scheduling (task-switching) points
Все-таки Java не была бы Java без излишнего ненужного переусложнения. Можно было и не добавлять новых сущностей virtual thread, просто сделали бы переключалку в виртаульной машине, с точки зрения разработчика поменяется только то что, что надо будет теперь везде в коде поменять thread на virtual thread.

Хорошо, что хотя бы от Fiber терминологии избавились. А был же еще Strand.

Ну они не могут просто так поменять конктракт. И правильно.
На самом деле очень элегантно врезали новую фичу на мой вкус. Вы и сами пишите:


с точки зрения разработчика поменяется только то что, что надо будет теперь везде в коде поменять thread на virtual thread.

только можно и не менять или если то достаточно мало и без риска

Sign up to leave a comment.

Articles