Темную силу чувствую я.
Даешь парсек за три года.
Привет! Меня зовут Владимир Фролов, я — Java Developer в DataArt. Этим материалом я хочу начать цикл публикаций на Highload о многопоточности в Java.
Нет, мы не будем рассматривать историю механических вычислительных машин для арифметических вычислений, созданных в XIX веке и раньше.
Начнем с XX века, когда появились компьютеры в привычном нам понимании. На заре компьютерной эры аналоговые и ламповые машины были большими, малопроизводительными и потребляли много электрической энергии. После открытия полупроводниковых материалов размеры ЭВМ уменьшились, а производительность и энергоэффективность существенно возросли. Затем начали появляться первые микросборки и микросхемы, а в 1965 году был сформулирован закон Мура, который гласил, что количество транзисторов на одном кристалле будет удваиваться каждые 24 месяца.
Один из основных элементов в компьютере — процессор. Он и выполняет компьютерные команды, которые составляют программу. В конце XX и в начале XXI века процессоры в основном были одноядерными. Тактовая частота у каждого нового процессора была больше, чем у предыдущей модели, за счет этого и возрастала общая производительность систем.
Когда у пользователей появилась потребность выполнять одновременно несколько программ на одноядерном процессоре (ядро которого в конкретную единицу времени может выполнять только одну инструкцию), был придуман способ добиться видимости решения проблемы. Трюк в том, что процессор переключается между выполнением команд из разных программ. Таким образом достигается видимость, что одноядерный процессор выполняет несколько действий или несколько программ одновременно, или в рамках одной программы выполняется несколько действий.
Но производство процессоров достигло технологического предела, когда дальнейшее уменьшение размеров транзисторов и повышение их тактовой частоты стало невозможным. Тогда производители и приняли решение об увеличении количества ядер в одном процессоре, чтобы увеличить производительность систем.
Рассмотрим на простейшем уровне, как работает процессор. Он состоит из:
Изначально АЛУ могло выполнять всего несколько элементарных операций: чтение и запись в память, сложение, сдвиг вправо, сдвиг влево, логические AND, OR, NOT, XOR.
Вычитать АЛУ не умело. Вычитание осуществлялось путем сложения в дополнительном коде, умножение — путем сложения и сдвига влево, деление осуществлялось сдвигом вправо и вычитанием.
Позже придумали аппаратные схемы, которые поддерживают эти операции и операции с числами с плавающей запятой. Самый главный вывод, который нужно сделать: даже самые простейшие операции как, например, сложение или вычитание двух чисел. производится не за одну команду процессора, а за несколько. Следовательно, операция может быть прервана, и процессор может начать выполнять команды из другой программы. То есть операции — неатомарные
Операционные системы могут решать задачу распределения процессорного времени разными способами. Основные подходы:
Введем понятия процесса и потока.
Процесс — это программа, которая выполняется в текущий момент времени, как и все ее элементы: адресное пространство, глобальные переменные, регистры, потоки, открытые файлы и т. д.
Поток (thread) — наименьшая единица обработки, исполнение которой может быть назначено ядром операционной системы. Или совокупность дискретного процессорного времени, в течение которого выполняются команды, или код для одной логической части программы.
Реализация потоков выполнения и процессов в разных операционных системах отличается друг от друга, но в большинстве случаев поток выполнения находится внутри процесса. Несколько потоков выполнения могут существовать в рамках одного и того же процесса и совместно использовать ресурсы, такие как память, тогда как процессы не разделяют этих ресурсов.
Потоки выполнения отличаются от процессов:
Когда процессор имеет несколько ядер, код действительно выполняется параллельно на разных ядрах, при этом каждое ядро выполняет один поток в конкретную единицу времени. При этом два разных ядра не могут выполнять один поток. Наличие большего количества ядер не гарантирует увеличения скорости выполнения программ. Если программа однопоточная, она будет выполняться на одном ядре, а остальные ядра в системе заняты не будут. Некоторые языки программирования поддерживают возможность назначение потока выполнения конкретному ядру процессора. Это называется thread affinity, но в Java нет такой возможности.
В статье мы кратко рассмотрели, как работает процессор и из чего он состоит, поддержку процессов и потоков в операционных системах, разобрались с основными понятиями и определениями многопоточности, с разницей между процессами и потоками.
В следующих статьях разберемся с потоками и их свойствами, блокировками и классами синхронизации потоков, атомарными переменными — читайте скоро на Highload.
В благословенные офисные времена, когда не было большой войны и коронавируса, люди гораздо больше общались…
Вот две истории из собственного опыта, с тех пор, когда только начинал делать свою карьеру…
«Ты же программист». За свою жизнь я много раз слышал эту фразу. От всех. Кто…
Отличные новости! Если вы пропустили, GitHub Copilot — это уже не отдельный продукт, а набор…
Несколько месяцев назад мы с командой Promodo (агентство инвестировало в продукт более $100 000) запустили…
Пару дней назад прочитал сообщение о том, что хорошие курсы могут стать альтернативой классическому образованию.…