- Однопоточный JavaScript и многопоточная Java: что быстрее?
- Асинхронное выполнение на Java и JavaScript
- Сравнение производительности многопоточных и однопоточных приложений
- Когда многопоточная Java работает медленнее, чем однопоточный JavaScript
- Параллельные HTTP-запросы при помощи древнего HttpURLConnection
- Параллельные HTTP-запросы при помощи современного HttpClient
- Параллельные HTTP-запросы на Node.js
- Почему разница между Java и JavaScript почти трёхкратная?
- Русские Блоги
- Однопоточный режим выполнения
- Русские Блоги
- В C # разница между многопоточностью (асинхронный режим) и однопоточностью (синхронный режим)
- Пример 1 однониточный
- Многопоточность
Однопоточный JavaScript и многопоточная Java: что быстрее?
Асинхронное выполнение на Java и JavaScript
При необходимости в JavaScript можно запускать дополнительные потоки. Но обычно в Node.js или в браузерах весь код на JavaScript выполняется в одном потоке. В браузерах один и тот же поток рендерит содержимое веб-страницы на экран. По сути, один поток выполнения занимается всеми задачами, потому что приложения JavaScript пользуются преимуществами асинхронного выполнения. Для асинхронного выполнения задача помещается в очередь задач. Задачи из очереди одна за другой выполняются единственным потоком. Например, вторая строка кода выполняет планирование асинхронной задачи, которая запускается после завершения текущей задачи:
Результатом работы кода будет 1 3 2 .
В Java API под асинхронным выполнением обычно подразумевается, что задача выполняется в новом выделенном потоке. Например, представленный ниже код при помощи метода supplyAsync() планирует асинхронную задачу:
Результат работы программы показывает, что текущий поток создал новый поток для выполнения задачи:
Проблема множественных потоков заключается в том, что Java runtime не может создавать бесконечное их количество. Когда все запущенные потоки ожидают, а новые потоки создать нельзя, приложение тоже ничего не будет делать. Чуть ниже я проиллюстрирую этот случай, но сначала мне бы хотелось упомянуть менее серьёзный, но более распространённый пример.
Сравнение производительности многопоточных и однопоточных приложений
Теоретически многопоточные приложения должны быть более производительными, чем однопоточные, но на практике это не всегда так. Возьмём в качестве примера основной способ применения Java — серверы приложений Java. В логе видно, что HTTP-запросы обрабатывает множество параллельных потоков с собственными именами. Но если развёрнутое веб-приложение выполняет операции ввода-вывода, то многопоточность по большей мере теряет смысл, поскольку доступ к файловой системе — это узкое «бутылочное горлышко». Десять потоков не могут быть производительнее одного потока, вынужденного ждать содержимого от файловой системы. Например, Java-сервер Tomcat при передаче статичных файлов проявляет себя не лучше, чем один инстанс Node.js.
Когда многопоточная Java работает медленнее, чем однопоточный JavaScript
Давайте попробуем скачать содержимое примерно ста случайных URL. При этом воспользуемся возможностью и сравним производительность древнего HttpURLConnection и современного HttpClient .
Представленный ниже код извлекает все абсолютные ссылки с https://www.bbc.com/news/world (около 100 URL), загружает их содержимое, а затем выводит общее время, потраченное на параллельное получение содержимого:
Также код выводит общий размер загруженного содержимого, чтобы убедиться, что разные способы загружают один и тот же контент. Самое важное для нас в коде — это измерение времени, необходимого для параллельного выполнения множества HTTP-запросов.
UrlTxt — это просто запись с двумя полями:
Метод getUrlsFromUrl() извлекает абсолютные URL из содержимого https://www.bbc.com/news/world:
Параллельные HTTP-запросы при помощи древнего HttpURLConnection
Для получения содержимого URL используется обычный код:
get() используется в подклассе общего родителя Runner . Чтобы использовать get() асинхронным образом, я применяю метод-адаптер load() . Кстати, обратите внимание на раздражающее ограничение стандартных функциональных интерфейсов — они не выдают исключений и реализующий их код часто необходимо оборачивать в некрасивые блоки try catch .
Функциональный код в requestManyUrls() адаптирован из самого современного рецепта по созданию параллельных запросов.
Результат работы кода:
Если повторно запустить тот же код, общий размер будет близким, но не точно таким же. Предполагаю, что содержимое некоторых ссылок динамично.
Параллельные HTTP-запросы при помощи современного HttpClient
Похоже, в настоящее время HttpClient — это лучший класс Java для создания HTTP-запросов. Кажется, он даже поддерживает HTTP/2, потому что иногда выдаёт ошибку HTTP/2 GOAWAY .
Огромный код с современным HttpClient выглядит пугающе, но по сравнению с предыдущим результатом в 6211 мс его работа радует:
Параллельные HTTP-запросы на Node.js
В браузере JavaScript не может скачивать содержимое с других хостов, если целевой хост этого не разрешил. Это мера безопасности. Сайт bbc.com не разрешает другим хостам получать его содержимое. Поэтому я использую только Node.js.
Посмотрите, насколько прост полный аналог предыдущего кода на JavaScript:
Что бы вы ни писали на JavaScript, преимущество очевидно — чем меньше клавиш мы нажимаете, тем меньше тратите времени и тем меньше вероятность внести баги. Однако так думают не все. Многие любят преобразовывать JavaScript в Java-подобный код под названием TypeScript.
Результат работы файла на JavaScript:
Почему разница между Java и JavaScript почти трёхкратная?
Код на JavaScript сначала выполняет один за другим 105 HTTP-запросов. Когда приходит ответ, движок JavaScript помещает в очередь задач небольшой обратный вызов. После получения всех ответов единственный поток по очереди обрабатывает их.
В Java это работает совершенно иначе. Создаётся множество потоков, каждый из которых отправляет один HTTP-запрос. После создания некого оптимального количества потоков стандартный оптимальный внутренний пул потоков больше не может создавать потоки. Несколько созданных потоков ждут ответов. Код ничего не делает. После поступления ответов создаются новые потоки для отправки новых запросов. И этот процесс повторяется, пока не будут отправлены все запросы. По сути, мой пример кода на Java (4910–1744)/4910=64% от общего времени не делает ничего, кроме как ждёт HTTP-откликов. Ситуация такая же, как и с вводом-выводом в серверах приложений Java, но для Интернет-содержимого время ожидания больше.
Если вы знаете, как реализовать более эффективные параллельные HTTP-запросы на Java, то напишите комментарий.
Источник
Русские Блоги
Однопоточный режим выполнения
Так называемый однопоточный режим выполнения означает «выполнение в одном потоке». Подобно тому, как одноплатный мост позволяет проходить одновременно только одному человеку, этот режим используется для установки ограничений, гарантирующих, что только один поток может выполнять обработку одновременно.
Небезопасные ворота:
Класс UserThread: люди, которые продолжают проходить через дверь
Класс теста: Основной
Когда экземпляр класса Gate используется несколькими потоками, результат выполнения не соответствует ожиданиям, то есть класс Gate небезопасен и не является потокобезопасным.
Почему идет не так:
Ситуация 1:Поток Алисы и поток Бобби выполняют проход:
Нить Алиса | Тема Бобби | значение this.name | значение this.address |
this.counter++ | this.counter++ | Предыдущее значение | Предыдущее значение |
this.name=name | “Bobby” | Предыдущее значение | |
this.name = name | Alice | Предыдущее значение | |
this.address= address |
Alaska this.address=addressAliceBrazilcheck()check()AliceBrazil ***** BROKEN *****
Ситуация 2:Поток Алисы и поток Бобби выполняют проход:
Нить Алиса | Тема Бобби | значение this.name | значение this.address |
this.counter++ | this.counter++ | Предыдущее значение | Предыдущее значение |
this.name=name | Alice | Предыдущее значение | |
this.name=name | Bobby | Предыдущее значение | |
this.address=address | Bobby | Brazil | |
this.address=address | Bobby | Alaska | |
check(); | check(); | Bobby | Alaska |
***** BROKEN ***** |
Заслонка нитевидного ремня безопасности:
Поток Алисы и поток Бобби выполняют синхронизированный модифицированный метод прохода:
Нить Алиса | Тема Бобби | значение this.name | значение this.address |
[Получить блокировку] | |||
this.counter++ | Предыдущее значение | Предыдущее значение | |
this.name=name | Alice | Предыдущее значение | |
this.adderss=address | Alice | Alaska | |
check() | Alice | Alaska | |
[Снять блокировку] | |||
[Получить блокировку] | |||
this.counter++ | Alice | Alaska | |
this.name=name | Bobby | Alaska | |
this.adderess=address | Bobby | Brazil | |
check(); | Bobby | Brazil | |
[Снять блокировку] |
Будь то синхронизированный метод или синхронизированный блок кода,
В любом случае, это можно рассматривать как получение блокировки на «<" и снятие блокировки на ">«.
Покажите, как обращаться с замками:
При возврате блокировку нельзя снять:
Когда doMethod выдает исключение, блокировку нельзя снять:
Напротив, синхронизированный метод и синхронизированный блок кода должны иметь возможность снимать блокировку независимо от того, выполняет ли он возврат или генерирует исключение.
После вызова метода lock (), независимо от того, какая операция выполняется, будет вызываться метод разблокировки;
Независимо от того, является ли приведенный выше код возвратом, исключением или любой другой операцией, будет вызываться часть finally.
Источник
Русские Блоги
В C # разница между многопоточностью (асинхронный режим) и однопоточностью (синхронный режим)
Что такое однопоточный? Однопоточный режим работы также становится синхронным. Единственное, что он может делать только одно в определенном состоянии, например, я могу готовить в период с 18:00 до 18:30.
Что такое многопоточность? Многопоточный режим работы также стал асинхронным. Он может делать несколько вещей в определенном состоянии, например, я могу готовить 18: 00-18: 30 и играть в игры в 18: 00-18: 20. Это в отличие от двух вещей, которые я выполнил за полчаса.
Пример 1 однониточный
Описание функции В одном потоке на сложение уходит 9 секунд, на вычитание — 4 секунды, всего 13 секунд.
Многопоточность
Определение блокировки: После завершения основного потока работа останавливается, пока дополнительный поток все еще работает. Трата 5 секунд = 9 секунд, чтобы выполнить сложение — 4 секунды, чтобы выполнить вычитание. Тем самым снижается эффективность работы основного потока 。
Пример 2: Многопоточность с блокировкой
Описание функции В одном потоке для сложения требуется 9 секунд, а для вычитания — 4 секунды. Но в многопоточности это занимает всего 9 секунд.
Пример 3, неблокирующая многопоточность (свойство IsCompleted)
Описание функции : Основной поток выполняет вычитание в течение 4 секунд и всегда использует свойство IsCompleted, чтобы проверить, завершен ли вторичный поток (то есть вычитание). Если вторичный поток не завершен, основной поток может выполнять другую работу, не блокируя и не теряя способность основного потока выполнять какие-либо действия.
Очевидно, что свойство IsCompleted инициируется основным потоком и проверяет вторичный поток. Когда вторичный поток только что завершил свою работу, основной поток потратит небольшое количество событий для вывода результатов вторичного потока (на этот раз это эквивалентно блокировке). Поэтому нам нужно найти более эффективный метод. Делегат AsyncCallback будет рассмотрен далее.
Пример 4. Неблокирующая многопоточность (делегирование AsyncCallback).
Описание функции : Делегат AsyncCallback возвращает значение из вторичного потока, чтобы указать основному потоку завершить свою работу. В это время основной поток может напрямую выводить результаты вторичного потока. В другие периоды (то есть, когда вторичный поток не возвращает значение), основной поток может обрабатывать другие вещи. Этот способ работы более эффективен.
Выведите идентификатор потока выполняемой функции Main: 10
Выведите идентификатор вторичного потока, который выполняет метод Add (): 6
Текущий основной поток работает над.
Текущий основной поток работает над.
Текущий основной поток работает над.
Текущий основной поток работает над.
Вывести идентификатор потока выполняемого метода AddComplete (): 6
Поток завершил свою работу
Текущий основной поток работает над.
Пример 5 На основании примера 4 передайте и получите пользовательские данные о состоянии.
Описание функции: используйте свойство AsyncState параметра AsyncResult для передачи и вывода выходного сообщения в методе BeginInvoke ()
Выведите идентификатор потока, который выполняет функция Main: 8
Выведите идентификатор вторичного потока, который выполняет метод Add (): 9
Текущий основной поток работает над.
Текущий основной поток работает над.
Текущий основной поток работает над.
Текущий основной поток работает над.
Текущий основной поток работает над.
Выведите идентификатор потока, который выполняет метод AddComplete (): 9
Поток завершил свою работу
Вы можете установить собственное сообщение здесь
Текущий основной поток работает над.
Пример 6 Используйте класс System.Threading.Thread, чтобы получить информацию о текущем выполняющемся потоке
Домен приложения текущего потока: ThreadStats.vshost.exe
Контекст текущего потока: 0
ThePrimaryThread
True
Normal
Running
ID потока, выполняемого в данный момент: 9
Пример 7 ThreadStart делегат
Описание функции: Позволяет пользователю выбрать одну или две нити для печати. Нужно загрузить используя System.Windows.Forms и используя System.Windows.Forms.DLL. Вот два файла классов:
Пример 8 ParameterizedThreadStart делегат
Описание функции: То же, что в примере 7, но это проще, чем метод написания, и его стоит рекомендовать. Пример 8 содержит два файла классов, Program.cs и AddParams.cs.
Пример 9 Класс AutoResetEvent
Описание функции: класс AutoResetEvent реализует эффективную и безопасную работу первичного / вторичного потока.
Пример 10 Передняя нить
Пример 11 Фоновая нить
Пример 12 Проблемы параллелизма
Описание функции : 10 потоков обращаются к одному и тому же объекту, что приводит к смешанному выводу.
Выходной результат ( Поскольку вывод слишком длинный, вывод будет выражен в фрагментах кода ):
Пример 13 ключ блокировки
Описание функции : 10 потоков обращаются к одному и тому же объекту, но использование ключевого слова Lock не приведет к смешанному выводу.
Program.cs То же, что и Program.cs примера 12
Выходной результат ( Поскольку вывод слишком длинный, вывод будет выражен в фрагментах кода ):
Пример 14 Ключевое слово монитора
Описание функции : 10 потоков обращаются к одному и тому же объекту, но использование ключевого слова Monitor не приведет к смешанному выводу.
Program.cs То же, что и Program.cs примера 12
Выходные результаты: То же, что в примере 13
Пример 15 : TimerCallback делегат + таймер таймера
Описание функции: печатать системное время каждую секунду.
***** Working with Timer type *****
Hit key to terminate.
Time is: 9:27:17
Time is: 9:27:18
Time is: 9:27:19
Time is: 9:27:20
Time is: 9:27:21
Time is: 9:27:22
Time is: 9:27:23
Time is: 9:27:24
Time is: 9:27:25
Time is: 9:27:26
Time is: 9:27:27
Time is: 9:27:29
Time is: 9:27:31
.
Пример 16 : Пул потоков CLR
Описание функции: Десять нитей системы вызываются непосредственно для печати номеров.
***** Fun with the CLR Thread Pool *****
Main thread started. ThreadID = 9
-> 6 is executing PrintNumbers()
Your numbers: 0, All tasks queued
1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 10 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 11 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 12 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 13 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 14 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 15 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 16 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 17 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-> 18 is executing PrintNumbers()
Your numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1. Пожалуйста, укажите источник этого блога, спасибо.
2. QQ контакт 479166938 в этой статье, пожалуйста, сообщите
Источник