Для того, чтобы успешно строить карьеру программиста, необходимо устроиться работать по соответствующей профессии, а также знать особенности языков программирования. Огромным спросом у разработчиков на данный момент пользуется Java. Это – кроссплатформенный универсальный вариант «общения» с компьютерами и программным обеспечением. Прост в освоении, обладает относительно понятным синтаксисом. Многофункционален, имеет возможность ООП.
Когда будущий разработчик проходит собеседование, ему задают разнообразные вопросы по коддингу. В случае с Java немаловажным моментом является тема потоков. Особенно многопоточности. Данная статья поможет разобраться в соответствующем направлении, а также составить приложение, использующее Java Concurrency.
Процесс – определение
Рассматривая упомянутую тему, нельзя обойти стороной такое понятие как «процесс». Это – основа, на которой базируется многопоточность.
Каждый программист должен уяснить следующее:
- Процесс представляет сочетание кода и информации. Он создается ОС при запуске приложения. Служит виртуальным адресным пространством.
- Процессы на задействованном устройстве функционируют обособленно друг от друга.
- Нет прямого доступа к общей информации в других процессах, работающих на девайсе.
- Для успешной работы требуется выделение тех или иных ресурсов – памяти и времени. Эти задачи возлагаются непосредственно на операционную систему.
- Если один из процессов блокируется, новый не может работать. Это происходит до тех пор, пока первый, заблокированный, не будет разлочен.
- Для создания очередного нового процесса принято использовать родительские. В ходе реализации поставленной задачи будет проведено дублирование.
- Process-родитель умеет контролировать дочерние операции. Обратная связь невозможна ни при каких обстоятельствах.
Эти принципы и особенности актуальны не только для операций, выполняющихся в приложении, написанном на Java. Под процессом принято понимать связь кода и информации, которые делят между собой адресное пространство.
Потоки – что это и с чем их едят
В Java поток – это единица реализации программного кода. Последовательность данных, которая могут работать параллельно с другими своими «аналогами».
Поток отвечает за выполнение инструкций запущенного процесса, к которому он относится. Все это происходит параллельно с иными потоками этого же process. Является легковесным. Может «общаться» с другими потоками. Для реализации поставленной задачи требуется применение специальных методов.
Какими могут быть потоки – классификация
Собеседование по Java и многопоточности обязательно предусматривает вопросы по «обычным» потокам в программировании. Стоит классифицировать оные. Это помогает разобраться, за что отвечает тот или иной «вариант».
Существует разделение потоков по типу движения информации:
- вводные – информация поступает в утилиту, после чего считывается;
- выводные – программа передает те или иные сведения, осуществляется запись в потоки.
Присутствует разделение по типу передаваемых электронных материалов. Не во всех случаях программисты используют байты. В Java и других языках может использоваться текст. На основании этого выделяют следующие потоки:
- символьные;
- байтовые.
У каждого приведенного примера существуют собственные абстрактные классы.
Принципы работы потоков
Перед использованием в Java многопоточности, нужно понимать, как будет работать каждая такая «операция». Многое зависит от того, какие манипуляции реализовываются. Чаще всего имеет место чтение и запись.
В Java потоки будут обладать примерно таким алгоритмом:
- Создается экземпляр необходимого потока.
- Последний открывается для дальнейшего считывания. При необходимости – для записи новой информации.
- Пользователь проводит задуманные изначально действия. В предложенном примере – чтение и запись информации.
- Осуществляется закрытие потока.
Создание и открытие экземпляра – это единый шаг. Остальные действия не реализовываются одновременно. Они воплощаются в жизнь последовательно.
Чем хороши потоки
В процессе программирования можно использовать различные элементы и операции. Потоки в Java имеют ряд преимуществ. К ним относят:
- Относительную легкость по сравнению с процессами. Это позволяет минимизировать ресурсные затраты для функционирования приложения.
- Быстрое переключение софта.
- Упрощенную схему взаимодействия процессов между собой.
У мелких утилит есть всего одна «нить». Это – главная нить (main thread). Дополнительно могут запускаться иные потоки. Они будут носить название дочерних. Главный thread отвечает за выполнение метода main, после чего завершается.
Определение многопоточности
Многопоточность Java – это поддержка одновременной работы более одного потока. В процессе выполнения приложения некоторые «операции» осуществляются параллельно друг другу, да еще и в «фоновом» режиме. Наглядные примеры:
- сигнальная обработка;
- операции с памятью;
- управление системой устройства/софта.
Приложение примет только первый поток. За счет многопоточности осуществляется одновременный прием и обработка нескольких потоков в рамках одной и той же утилиты.
Важно: многопоточность не имеет места с процессорами одноядерного типа. Там время процессорного характера делится между несколькими процессами и «открытыми» потоками.
Потоковая синхронизация – как понимать?
Определение синхронизации потоков имеет прямое отношение к многопоточности. Синхронизированный «участок» программного кода выполняется только одним потоком одновременно.
В Java присутствует поддержка одновременного выполнения нескольких подобных «элементов». Соответствующее явление иногда приводит к тому, что больше одного потока имеет доступ к одним и тем же полям/объектам.
Синхронизация – выполнение параллельных потоков в приложении синхронно. Способствует избеганию ошибок согласования памяти. При объявлении метода синхронизированным нить держит монитор для объекта. Если иной поток займется исполнением синхронизированного метода, первый будет блокирован.
Синхронизацию в Java реализовывают через зарезервированное слово synchronized. Он может применяться в классах для определения синхронизированных:
- блоков;
- методов.
Не применяется соответствующее слово ни в переменных, ни в атрибутах в процессе определения того или иного класса.
Потоковые состояния
Thread в Java встречается в нескольких состояниях:
- new – создан;
- runnable – запуск;
- blocked – блокировка;
- terminated – завершение;
- waiting – ожидание.
Первый «статус» присваивается при создании экземпляров класса, второй – после запуска и начала процессорной обработки. При «блокировке» поток ожидает высвобождения ресурсов, а также завершения ввода-вывода информации. При terminated происходит завершение работы без перспектив повторного запуска.
Также можно встретить Suspend. Это процесс приостановки работающего потока. Предусматривает продолжение с момента «паузы». Состояние Dead в Java возникает при полном прекращении работы Thread. Указывает на то, что жизненный цикл «объекта» подошел к концу.
Concurrency – библиотека для работы со Threads
У Джавы немало документации на русском языке, при помощи которой можно разобраться в принципах работы с языком. И там обязательно рассказывается о многопоточности. В процессе чтения соответствующей информации программеры встречают такое понятие как Concurrency.
Так называют специальную библиотеку Java. В ней собраны спецклассы, предназначенные для работы с несколькими нитями. Они включены в пакет java.util.concurren. Включают в себя различные элементы:
- Concurrent Collections – коллекции, предназначающиеся для работы с многопоточностью. В процессе работа используется принцип блокировки по сегментам информации. Возможна оптимизация параллельного чтения по wait-free алгоритмизации.
- Synchronizers – вспомогательный контент. Задействуется непосредственно при синхронизации потоковой информации. Особо важен для параллельных вычислений.
- Queues – очереди блокирующего и неблокирующего характера для многопоточности. Первый вариант применяет для «тормоза» потоков, если не проходят реализацию те или иные условия. Второй актуален для обеспечения скорости обработки информации. Функционирование в данном случае будет осуществляться без потоковой блокировки.
- Executions – фреймворки, использующиеся для создания потоковых пулов, а также при планировании асинхронных задач, для которых нужно выводить результаты.
- Locks – альтернативные способы синхронизации.
- Atomics – классы, поддерживающие атомарные операции со ссылками/различными примитивами.
Каждый «пакет» имеет собственные классы Java. Они отвечают за те или иные манипуляции при коддинге. Полную информацию о них можно изучить по этой ссылке.
Как создавать потоки: способы реализации
Пока не рассмотрены примеры многопоточности в языке Java, стоит уяснить, каким образом создаются threads. Существуют различные варианты развития событий. Все зависит от того, какие задачи предстоит реализовывать.
Создание потоковых «элементов» возможно через:
- класс, реализующий Runnable;
- классы, расширяющие Thread;
- реализацию java.util.concurrent.Callable.
Первый вариант встречается на практике чаще всего. Связано это с тем, что Java реализует интерфейс не в единственном количестве. Это дает возможность наследования классов.
Метод Runnable
На практике все перечисленные способы создания threads не слишком трудно реализовать. В случае с Runnable потребуется:
- создать объект класса thread;
- сделать class object, который реализовывает интерфейс Runnable;
- вызвать метод start() у объекта thread.
Все это поможет сделать new thread, а затем использовать его.
Метод Thread
Еще один вариант – наследование. Для этого предстоит:
- сделать class object ClassName extends Thread;
- обеспечить предопределение run() в соответствующем классе.
Позже будет приведен пример, в котором осуществляется передача имени потока «Second»
Через Concurrent
В этом случае потребуется:
- сделать объект класса, работающего с интерфейсом Callable;
- обеспечить создание ExecutorService, в котором пользователь указывает пул потокового характера;
- создать Future object.
Последний шаг осуществляется путем внедрения метода submit.
Наглядные примеры
Вот коды, которые пишем для создания new threads:
public static void howToRunThreads() {
ThreadClass threadClass = new ThreadClass("First");
threadClass.start(); //method ThreadClass.run()
Thread thread = new Thread(new RunnableClass("Second"));
Thread thread2 = new Thread(new RunnableClass("Third"));
Thread thread3 = new Thread(new RunnableClass("Fourth"));
thread.start(); //method RunnableClass.run()
thread2.start(); //method RunnableClass.run()
thread3.start(); //method RunnableClass.run()
}
public class RunnableClass implements Runnable {
private String localName;
public RunnableClass() {
}
public RunnableClass(String localName) {
this.localName = localName;
}
@Override
public void run() {
System.out.println("run() " + localName + " running");
}
public String getLocalName() {return localName;}
public void setLocalName(String localName) {this.localName = localName;}
}
public class ThreadClass extends Thread {
public ThreadClass() {
}
public ThreadClass(String name) {
super(name);
}
public ThreadClass(Runnable target) {
super(target);
System.out.println(target + " will running");
}
@Override
public void run() {
System.out.println("ThreadClass run() method " + "Thread name is: " + this.getName());
}
}
//Выведение
ThreadClass run() method Thread name is: First
run() Third running
run() Fourth running
run() Second running
Если же нужно применять Callable, стоит обратить внимание на следующую кодификацию:
Для синхронизации методов можно реализовывать следующий код:
Для многих программистов при начале изучения многопоточных «элементов» Java становится проблемой принудительная остановка. На самом деле добиться желаемого результата не так трудно.
О принудительной остановке Thread
В Java 8 отсутствуют методы, при помощи которых можно добиться принудительной остановки. Но можно воспользоваться специальным механизмом, который позволяет вмешаться в потоковые процессы. Достаточно использовать interruption e. Это – механизм потокового оповещения.
У Thread есть булево поле – флаг прерывания. Устанавливается через вызов interrupt(). Проверить факт его задействования можно несколькими методами:
- через bool isInterrupted() потокового объекта;
- используя bool Thread.interruped().
В первом случае происходит возврат флага и его сброс. Во втором вызов осуществляется внутри thread, из которого был вызван метод. Данный прием дает возможность проверки состояния потокового прерывания в Java.
Здесь происходит следующее:
- В методе main() создается объект класса JoinClass, запускаемый через run.
- Происходит проверка на факт завершения. Каждые 100 секунд на экран выводится информация о значении счетчика.
- Главный метод ждет 1 000 мс для того, чтобы счетчик мог произвести расчеты.
- Осуществляется вызов interrupt у JoinClass.
- В цикле обнаруживается исключение.
- В разделе catch активируется return.
По принудительной потоковой остановке тоже много документации, как и по Java Concurrency гайдов на русском.
Как лучше понять многопоточность – совет не только новичкам
Для того, чтобы такие понятия, как interruptedexception e, multithreading (многопоточность) и процессы не доставляли хлопот, стоит выбрать грамотную методику обучения. Лучшим решением сегодня является посещение специализированных курсов. После них пройти собеседование (interview) по Java не составит никакого труда.
Пользователи могут выбрать специализацию и направление, в котором работать. Есть предложения для тех, кто только начал путь изучения программирования, а также для продвинутых юзеров. С их помощью познать Java Concurrency на практике не составит никакого труда, ведь процесс обучения контролируется опытными кураторами. В конце выдается сертификат установленного образца. Все лекции на русском.
Преимущества подобного подхода:
- доступность;
- удобство – слушать лекции можно в любое время в любом месте, где есть подключение к Сети;
- практика;
- наличие опытных кураторов-программистов;
- возможность выбора курса с учетом спектра имеющихся знаний.
Это – отличный старт для программиста-новичка, а также для «любителей», предпочитающих самообразование.