Pull to refresh

Comments 27

практически все рассказывающие о виртуальных потоках отмечают, что их не рекомендуется применять для кода с большой интенсивностью и количеством syncronized блоков (не знаю понятно ли это из объяснения в статье, но если коротко: блокирующая операция в синхронизированном блоке "приклеивает"(литературный перевод pin), т.е. блокирует платформенный поток) и вообще они предполагаются не для задач использующих общие ресурсы и не для замены платформенных потоков, а тут кажется они "наоборот" пытаются сделать из томкета nodejs

а тут кажется они "наоборот" пытаются сделать из томкета nodejs

Скорее как таски с async-await в dotnet-е. Там запретили в async методах использовать lock для решения этой проблемы.

таски с async-await в dotnet-е. Там запретили в async методах использовать lock для решения этой проблемы.

Если быть точным, то не запретили.

Запретили использовать await внутри lock:

async Task<int> DoAsync()
{
	lock (this)
	{
		await Task.Delay(10); // CS1996 Cannot await in the body of a lock statement
	}
	
	return 7;
}

А lock внутри async - пожалуйста:

async Task<int> DoAsync()
{
	await Task.Delay(10);

	lock (this)
	{
		// do something
	}

	await Task.Delay(10);

	return 7;
}

Инженеры Netflix отправили несколько независимых отчетов о периодических таймаутах и случаях подвисания в команды

А до этого инженеры Нетфликс думали что красивое название

Виртуальные потоки

По щучьему велению решит всё проблемы асинхронного кода.

Вы будете смеяться, но как только разберутся с synchronized (а это с очень высокой вероятностью произойдет в ближайших релизах https://news.ycombinator.com/item?id=39012631), то решит если не "всё" проблемы многопоточности, то по крайней мере избавит нас от необходимости писать жуткий асинхронный код на колбэках, при этом не мучаясь с async await, которые в других языках делят код на два мира.

я боюсь что, действительно, можно будет только посмеяться.

Мне,например, довелось и 20+ лет назад работать с кодом который очевидно избежал

необходимости писать жуткий асинхронный код на колбэках

при использовании многопоточности, когда async await -ов еще в помине не было.

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

Вполне вероятно, что ваш код 20+ лет был обычным кодом на потоках ОС. Многопоточный код на потоках ОС - это ОК. Но если таких потоков создать много, то программа дальше функционировать не сможет, поскольку ресурсы закончатся. А много их создать хочется, потому что значительную часть времени потоки находятся в процессе выполнения I/O операций, а процессор простаивает. Т.е. естественным желанием является увеличить количество потоков обработки, чтобы загрузить процессор, но в классической модели так сделать не получится.

Именно эту проблему решает "жуткий асинхронный код на колбэках", который позволяет оптимально загрузить железо ценой удобства разработки. И именно эта цена, которую пришлось заплатить, не устраивала значительное количество людей по всему миру, поэтому и появились async/awaitы, горутины и т.д, которые позволяли писать "как-бы" синхронный код, в котором I/O операции не требовали держать поток ОС.

В Java аналога такой функциональности "как-бы" синхронного кода с оптимальной загрузкой потоков ОС долго не было, пока не появился Loom. Его основное преимущество по сравнению с другими языками, например C# async/await в том, что никаких дополнительных ключевых слов использовать не нужно и JVM всё делает за вас, просто беря ваш код и понимая, в какие моменты его выполнение нужно "отцепить" от потока ОС (например, на операции ввода/вывода) и когда "прицепить" обратно.

Концептуально великолепно, но то, что ушло в 21 версии в релиз к сожалению не умеет правильно обрабатывать synchronized блоки/методы и поскольку значительное количество (в первую очередь старых) библиотек их используют, то это приводит к таким вот ситуациям, которая описана в статье. Проблема с synchronized непростая, но не нерешаемая и ее скоро решат. И вот тогда, действительно можно будет говорить о том, что "всё" проблемы многопоточности (применительно к конкретной ситуации оптимального использования железа для выполнения обычного синхронного кода) действительно будут решены.

Вполне вероятно, что ваш код 20+ лет был обычным кодом на потоках ОС.

Без всякого "вероятно" я совершенно определенно могу сказать что этот 20+ лет код (только не был, а есть, и возможно даже что даже вы периодически пользуетесь продуктами на его основе) сделан на интерфейсах. Дело в том что те интерфейсы спроектированы так что вы не знаете есть ли поток под данным конкретным интерфейсом или он работает совершенно синхронно, просто надо уметь проэктировать правильное взаимодействие и вообще архитектуру на интерфейсах, раньше это умели делать, а теперь ищем способ кто бы сделал это за нас.

Казалось бы причем здесь интерфейсы?... То о чем мы говорим - это не вопрос использования/не использования абстракций, а вполне конкретный вопрос о том, как сделать так, чтобы программа выглядела синхронной (с использованием интерфейсов или без них), но при этом не занимала потоки ОС в моменты ожидания. Раньше такого действительно не было, может только если какой-нибудь Erlang так умел, но в Java да и в других языках 20+ лет назад о таком даже не думали, потому что проблемы и приоритеты были другие.

Я имел ввиду, что не думали о решении такой проблемы: как бы нам сделать так, чтобы синхронно написанный код работал асинхронно. Green Threads эту проблему не решали.

Спасибо переводчику за хорошую статью, так бы я ее конечно не нашел... но читать лучше в оригинале, простите.

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

Когда решится проблема с synchronized, а она решится в ближайшем будущем, то это действительно будет так.

вам плюс за оптимизм, в любом случае.

Это не оптимизм, а реализм. Во-первых Loom уже сейчас работает вполне неплохо и если у вас везде используются только lockи, без synchronized вы уже прямо сейчас можете наблюдать то самое "повышение производительности" (а если точнее, более оптимальное использование железа при большом количестве I/O операций). Во-вторых ничего сверхсложного в том, чтобы synchronized тоже "починился" нет, поскольку в конечном счете его можно на уровне Jvm "свести" к операциям с локами, которые даже текущий Loom поддерживает.
Да и в целом если вы задумаетесь про Loom на концептуальном уровне - есть ли у JVM в рантайме вся информация для того, чтобы понять, что ваш поток сейчас чего-то ждёт и процессор не занимает? Конечно есть! В реализации могут быть нюансы, первый блин комом, но концептуально здесь всё довольно просто. Вспомните parallel Streamы, которые на релизе в J8 работали значительно медленнее, чем в J9.
Так что я стараюсь реалистично смотреть на эту ситуацию.

Без сарказма. Можете поделиться источниками, как и когда проблема будет решана?

Сейчас идёт активная дискуссия, о том, как решить эту проблему технически, например вот один из тредов на эту тему: https://mail.openjdk.org/pipermail/loom-dev/2024-February/006433.html . Лично у меня нет сомнений, что эту проблему решат, поскольку с одной стороны, проблема действительно большая, а с другой Loom больше не preview фича и что-то сделать им точно придётся. Я предполагаю что следующий LTS уже будет выпущен с решением этой проблемы.

Спасибо! Было бы здорово.

Она уже решена, вы можете скачать jdk23-ea и собрать самостоятельно, все интеграционные тесты она проходит. Релиз выйдет через месяц.

Скрытый текст

https://mail.openjdk.org/pipermail/loom-dev/2024-February/006433.html

В 23 джаву насколько я знаю не вошло. Скорее всего будет в 24.

Это решится на уровне JDK?

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

А чему ломаться, если ресурсы позволяют? https://github.com/smallnest/C1000K-Servers вот пожалуйста люди еще 9 лет назад экспериментировали и разгоняли на одной машинке 1.2 миллиона вебсокет конекшенов в том числе на Jvm и всё в порядке. Вот только на код там без слез нельзя смотреть и без мануала разобраться, это да. Идея Loom в том, что можно обойтись и без слёз при этом не сильно меняя свой код.

Это ж обычные фиберы, в Винде они наверное ещё со времён NT, как и в линуксах с их swapcontext. Разрабы джавы не смогли в нормальный планировщик фиберов на пуле потоков? Вообще, переход на асинхронность должен менять мозг разработчиков в части устройства платформы. Для качественной работы там надо убирать все блокировки, заменяя на нотификации, а "спать" реальный поток должен только в одном случае - когда нет заданий в очереди. Не получилось задаче захватить ресурс - подпишись на его освобождение и отдай поток другой задаче.

нагуглил статейку в которой рассказывается как обнаруживать запинивающиеся потоки:

https://todd.ginsberg.com/post/java/virtual-thread-pinning/

Sign up to leave a comment.