Основы программирования на Java
Основы программирования на Java
ОглавлениеВВЕДЕНИЕОСНОВНЫЕ ПОНЯТИЯ 1. ПЕРЕМЕННЫЕ 2. ТИПЫ 2.1. Простые типы 2. 2.1.2. Символы 2.1.3. Тип boolean 2.2. Приведение типов 3. МАССИВЫ 3.1. Многомерные массивы 4. ОПЕРАТОРЫ 4.1. Арифметические операторы 4.1.1. Оператор деления по модулю 4.1.2. Арифметические операторы присваивания 4.1.3. Инкремент и декремент 4.2. Целочисленные битовые операторы и операторы отношений 4.3. Операторы отношений 4.4. Булевы логические операторы 4.5. Тернарный оператор if-then-else 4.6. Приоритеты операторов 5. УПРАВЛЕНИЕ ВЫПОЛНЕНИЕМ ПРОГРАММЫ 5.1. Условный оператор if-else 5.2. Опреатор break 5.3. Оператор switch 5.4. Оператор return 6. ЦИКЛЫ 6.1. Цикл while 6.2. Цикл do-while 6.3. Цикл for 6.4. Оператор continue 7. КЛАССЫ 7.1. Переменные класса 7.2. Оператор new 7.3. Объявление методов 7.4. Вызов метода 7.5. Скрытие переменных 7.6. Конструкторы 7.7. Совмещение методов 7. 8. Ссылка this 7.9. Наследование 7.10. Ссылка super 7.11. Замещение методов 7.12. Динамическое назначение методов 7.13. Директива final 7.14. Деструкторы 7.15. Статические методы Директива final, деструкторы и статические методы 7.16. Абстрактные классы 8. ПАКЕТЫ И ИНТЕРФЕЙСЫ 8.1. Пакеты 8.2. Интерфейсы 9. ОБРАБОТКА ИСКЛЮЧЕНИЙ 9.2. Типы исключений 9.3. Неперехваченные исключения 9.4. Операторы try и catch 9.5. Несколько разделов catch 9.6. Вложенные операторы try 9.7. Оператор throw 9.8. Оператор throws Операторы throw и throws 9.9. Оператор finally 10. МНОГОПОТОЧНОЕ ПРОГРАММИРОВАНИЕ 10.1. Модель легковесных процессов в Java 10.2. Подпроцесс 10.3. Интерфейс Runnable 10.4. Приоритеты подпроцессов 10.5. Синхронизация 10.6. Методы программного интерфейса легковесных процессов 11. ВВОД/ВЫВОД 11.1. Работа с файлами 11.2. Каталоги Классы InputStream и OutputStream 11. 11.4. Класс OutputStream Файловый поток FilelnputStream и FileOutputStream 11.5. Файловый поток FilelnputStream 11.6. Файловый поток FileOutputStream 12. ПРОГРАММИРОВНИЕ ГРАФИЧЕСКИХ ПОЛЬЗОВАТЕЛЬСКИХ ИНТЕРФЕЙСОВ 12.1. Компоненты 12.2. Класс Container 12.3. Класс Canvas 12.4. Класс Label 12.5. Класс Button 12.6. Класс Checkbox 12.7. Класс CheckboxGroup 12.8. Класс Choice 12.9. Класс List 12.10. Класс Scrollbar 12.11. Класс TextField 12.12. Класс TextArea 12.13. Стратегии размещения компонентов 12.15. Программирование меню 12.16. Модель обработки событий от компонентов ЗАКЛЮЧЕНИЕ БИБЛИОГРАФИЧЕСКИЙ СПИСОК |
Потоки, атомарность, синхронизация | Vesel.ROGER.
Потоки (threads)
Начнём с простого. Что такое программа? Это последовательность выполняемых инструкций. Например, возьмём простую программу, которая каждые 5 секунд выводит информацию о том, что прошло 5 секунд:
Давайте её запустим и откроем jvisualvm. Если в списках процессов Java трудно найти нужный Main, можно класс переименовать (как сделал я) в “Timer“. Раскроем в jvisualvm нужный процесс и перейдём на вкладку “Threads”:
Как мы видим, у нас 11 потоков, 10 из них демон-потоки (Daemon threads). Давайте теперь разбираться, что же это с вами такое видим.
Для каждой запущенной программы с операционной системе создаётся процесс. У каждого процесса есть хотя бы один поток. Поток, с которого начинается программа называется главным (Main). Программа в Java завершается тогда, когда завершается последний поток. Последний нормальный поток. Есть так же потоки – демоны. Это обычно сервисные потоки для выполнения сервисных задач. Более подробно в статье “Многопоточность в Java“. Если посмотреть на скриншот, то видно, что есть наш обычный поток main, а есть демон потоки. Например, системный демон-поток “Finalizer” занимается вызовом метода finalize для объектов, собираемых сборщиком мусора (Garbage Collector’ом), когда на эти объекты больше не осталось ссылок.
Что же делать, если хочется, чтобы наше приоложение стало многопоточным? Например, если мы хотим добавить запись в файл, но так, чтобы это не мешало нашему таймеру. Существует несколько вариантов.
Первый вариант – реализовать интерфейс Runnable. Пример в документации тут. Такой способ подходит для общих случаев, т.к. реализуя интерфейс мы одновременно позволяем выполняться ему в потоке, при этом можем использовать свою иерархию наследования. Как мы помним, в Java нет множественного наследования. А это позволит не накладывать рамки.
Перенесём код из нашего метода main в наш новый Runnable:
Но, как мы понимаем, это ещё просто некий класс, который выполняет контракт Runnable. Если мы создадим новый TimerRunnable и выполним run – чуда не прозойдёт. Нужно создать поток, в котором будет запущен данный код:
Теперь мы в списке увидим понятное название “TimerThread”.
Пример через расширение Thread:
Но Runnable предоставляет более гибкий способ управления. Например, запуск через Executor’а и пул потоков (т.к. создание Thread это дорого):
Есть полезная возможность запуска Runnable по расписанию (пример с отложенным на 1 секунду стартом):
Задача с 5 секундами может быть выполнена ещё удобнее:
На самом деле есть ещё интересный интерфейс java.util.concurrent.Callable. Он сход с Runnable, но позволяет возвращать результат. А так же есть интерфейс Future, который предоставляет возможность получать результат (метод get), получать статус (isCancelled, isDone), а так же выполнять попытку отмены (cancel).
Интересно, что имея только лишь Callable за нас реализацией Future может заниматься сам Executor. Находка для ленивых ) . И вот пример:
Ссылки на ещё материалы: тут и тут.
Управление потоками
Потоки это хорошо, но ими надо как-то управлять. Отличное описание в статье “Многопоточность в java“.
Что можно от туда интересного выписать? Прервывание поток, Interrupted, удобная штука. Вообще, бесконечные циклы, такие как while(true) – неприличная вещь. Лучше добавлять условие. Поэтому, мы в нашем TimerRunnable можем изменить наш while цикл так:
И тогда так в main методе напишем:
Необходимо не забыть выполнить shutdown для пула потоков, т.к. после выполнения Runnable в пуле останется “припаркованный” поток. Пока он не завершится – наша программа будет работать вечно.
Для управления потоками так же есть базовые готовые варианты. Они отлично визуально продемонстрированы здесь: “Справочник по синхронизаторам java.util.concurrent.*“. Приведу лишь простой пример, чтобы было понятно, что за “готовые варианты” там описаны:
Изменяемость (volatile)
Как можно предположить, иногда несколько потоков могут обращаться к одним и тем же данным. И это может привести к тому, что у потока могут оказаться не актуальные данные. Что приведёт к неправильным данным. И тут придёт на помощь volatile. На одном форуме нашёл хорошое объяснение необходимости ключевого слова volatile:
Так же отличное описание можно прочитать про Volatile здесь.
И вот, чтобы можно было проверить, пример (а то все пишут зачем, но найти пример сразу не просто на просторах интернета):
Атомарность
При слове атомарность сразу ассоциация с атомом. В химии это наименьшая частица, от греческого слова ἄτομος, что переводится как неделимый. Атомарные операции по той же причине так называются – они неделимы. В интернете нашёл отличное определение: “Атомарные операции- это операции, которые не могут быть прерваны планировщиком потоков.”. Планировщик потоков (Thread Scheduler) отвечает за выполнение всех потоков. Он решает, какой поток запускать и т.п. Вот тут Oracle рассказывает про Atomic Action.
Атомарные оболочки
В Java есть специальный пакет package java.util.concurrent.atomic, который содержит оболочки для выполнения атомарных операций. Для примера можно взять атомарную оболочку для Integer.
Данные оболочки используют принцип CAS – Compare And Swap (Сравнение и замена). Суть мы увидим из кода AtomicInteger. Для начала, увидим:
Т.е. внутри у AtomicInteger есть volatile (видимый всеми потоками) примитивный integer. А вот так выглядит CompareAndSet:
Данный метод при помощи нативного метода из sun.misc.Unsafe в случае, если текущее значение value в AtomicInteger равно expect (т.е. никто его не изменил), то тогда оно будет обновлено на update. Об успешности он сообщит в boolean ответе.
Базовые примеры приведены для AtomicLong тут.
Синхронизация, блокировки и мониторыСледующим важным пунктом является синхронизация. Разработчик, чтобы её включить, должен указать ключевое слово synchronized. Но исходя из целей сделать он может это разными способами.
Отличное описание есть от JavaRush и ещё немного в этих вопросах.
Описание блокировок (LOCK) описано тут и про ReentrantLock.
Статья про Concurrency Lock API на сайте javadevblog.com.
Дополнительно, “10 советов по многопоточности“.
Думаю, для вводного объёма вполне хватит.
Like this:
Like Loading…
Документация JDK 20 — Главная
- Главная
- Ява
- Java SE
- 20
Обзор
- Прочтите меня
- Примечания к выпуску
- Что нового
- Руководство по миграции
- Загрузить JDK
- Руководство по установке
- Формат строки версии
Инструменты
- Технические характеристики инструментов JDK
- Руководство пользователя JShell
- Руководство по JavaDoc
Язык и библиотеки
- Обновления языка
- Основные библиотеки
- HTTP-клиент JDK
- Учебники по Java
- Модульный JDK
- Руководство программиста API бортового регистратора
- Руководство по интернационализации
Технические характеристики
- Документация API
- Язык и ВМ
- Имена стандартных алгоритмов безопасности Java
- банок
- Собственный интерфейс Java (JNI)
- Инструментальный интерфейс JVM (JVM TI)
- Сериализация
- Проводной протокол отладки Java (JDWP)
- Спецификация комментариев к документации для стандартного доклета
- Прочие характеристики
Безопасность
- Руководство по безопасному кодированию
- Руководство по безопасности
Виртуальная машина HotSpot
- Руководство по виртуальной машине Java
- Настройка сборки мусора
Управление и устранение неполадок
- Руководство по устранению неполадок
- Руководство по мониторингу и управлению
- Руководство по JMX
Client Technologies
- Руководство по специальным возможностям Java
Разница между интерфейсом Thread и Runnable в Java
Thread и Runnable в Java Решение всегда было запутанным для новичков в Java. Thread в Java кажется простым по сравнению с Runnable, потому что вы просто имеете дело с одним классом java.lang.Thread , в то время как в случае использования Runnable для реализации Thread вам нужно иметь дело с двумя классами Thread и Runnable. хотя решение об использовании Runnable или Thread следует принимать с учетом различий между Runnable и Thread , а также плюсы и минусы обоих подходов.
Это также очень популярный вопрос для интервью, и большинству интервьюеров действительно интересно узнать, какова ваша точка зрения при выборе Thread vs Runnable или наоборот .
В этой статье по java мы постараемся указать на некоторые различия между Thread и Runnable в Java , которые помогут вам принять взвешенное решение.
Кстати, если вы серьезно относитесь к освоению многопоточности и параллелизма в Java, я также предлагаю вам взглянуть на эти курсы Java Multithreading. Этот список содержит расширенные курсы, чтобы стать экспертом в области многопоточности, параллелизма и параллельного программирования на Java с упором на высокую производительность.
Вот некоторые из моих мыслей о том, следует ли мне использовать Thread или Runnable для реализации задач в Java, хотя у вас есть еще один вариант — «Callable» для реализации потока, который мы обсудим позже.
1. Java не поддерживает множественное наследование. Это означает, что вы можете расширять только один класс в Java, поэтому после расширения класса Thread вы потеряли свой шанс и не можете расширять или наследовать другой класс в Java.
2. В объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не вносим никаких изменений в Thread, вместо этого используйте интерфейс Runnable.
3. Интерфейс Runnable представляет задачу, которая может выполняться либо простым потоком, либо исполнителями, либо любым другим способом. поэтому логическое разделение Task как Runnable, чем Thread, является хорошим дизайнерским решением.