Core-2: Concurrency Flashcards
Что такое многопоточность?
Concurrency - Принцип построения программы, при котором несколько блоков кода могут выполняться одновременно.
В одноядерном процессоре: Context Switch
В многоядерном процессоре: Context Switch + Parallelism
Синхронно - последовательно
Асинхронно - параллельно
Что такое и чем процесс отличается от потока? (4)
Процесс - это экземпляр программы, создаваемый и исполняемый операционной системой.
Поток - подпроцесс, определяющий последовательность исполнения кода в процессе.
Поток: new-> runnable(ready/running)-> terminated.
JVM, испоняющая байткод - процесс, а GC в ней - поток
Различия:
1) Процессы - независимые сущности, существующие в своем адресном пространсве, потоки существуют в рамках адресного пространства содеращего процесса.
2) процесс не может напрямую обращаться к ресурсам другого П.,
процессы обмениваются данными через конвейеры, файлы, каналы связи между компьютерами и многое другое.
Поток может обращаться ко всем ресурсам содержащего прцесса.
3) Процесс гораздо тяжелее потока, его запуск и содержание обходится гораздо дороже.
4) Потоки подходят для параллелизации задач в рамках процесса, процессы служат для параллельно исполнения взаимонезависимых задач.
Что такое потоки-демоны?
В отличии от пользовательских потоков, которые явно создаются пользователем, демоны создаются системой для обеспечения вспомогательных действий (garbage collector)
JVM принудительно завершит всех демонов, когда будет завершён последний пользовательский поток.
Чем Thread отличается от Runnable? Когда нужно использовать Thread, а когда Runnable?
(Ответ что тред - это класс, а ранбл интерфейс - считается не
полным, нужно рассказать подробно)
Runnable – это функциональный интерфейс с единственным методом public void run(). Экземпляр R. не является потоком, а просто формулирует задачу, которую можно выполнить в отдельном потоке.
Thread implements Runnable – это класс, содержащий методы для запуска и управления состоянием потока, экземпляр которого мы можем создать и запустить в отдельном потоке, переопределив метод run()->Thread implements Runnable.
Чтобы использовать несколько потоков мы можем:
~~~
Class MyThread extends Thread{
@Override public void run(){sout(“new thread strarts”);}
psvm{
MyThread myThread = new MyThread();
myThread.strart();}}
or
Class MyRunnable implements Runnable{
@Override public void run(){sout(“new thread strarts”);}}
psvm{
// myRunnable- описание задачи, которая может быть исполнена в myThread:
MyRunnable myRunnable = new MyRunnable();
// myThread - исполняемая сущность класса Thread:
Thread myThread = new Thread(myRunnable);
myThread.strart();}
~~~
1) extend Thread избавляет от лишней строчки при создании экземпляра Thread, а implement Runnable позволяет классу наследоваться от другого класса, кроме Thread.
2) runnuble позволяет отделить логику выполнения задачи от непосредственного управления потоком, так как экз. R. - задача, которую может исполнить поток, и ей нет дела до того, как именно организована работа этого потока.
3) Есть практика, что если нет необходимости переопределятьу Thread что-то кроме run(), то лучше implement Runnable.
Что такое монитор? Как монитор реализован в java?
Монитор – механизм синхронизации потоков, обеспечивающий упорядоченный доступ к общим ресурсам по принципу mutex (mutual exclusion).
Монитор встроен в класс Object и имеется у каждого объекта или класса. Он имеет 2 состояния: занят и свободен.
В Java монитор реализован с помощью ключевого слова synchronized, причем ни методы ни блоки не имеют монитора, используется монитор this в методе неявно, и в блоке явно. в static class используется монитор класса.
Что такое синхронизация? Какие способы синхронизации существуют в java?
Синхронизация – это процесс, который позволяет выполнять потоки параллельно:
все объекты имеют блокировку, благодаря которой только один поток одновременно может получить доступ к критическому коду в объекте.
Способы:
1. Системная синхронизация с использованием wait()/notify().
wait - освобождает монитор и переводит вызывающий поток в состояние ожидания, пока другой поток не вызовет notify() или не истечет время в параметре: wait(1000 ms)
notify - не освобождает монитор, но “будит” один спящий или ожидающий поток и позволяет ему поучаствовать в битве за монитор.
NB: оба этих метода надо вызывать на том объекте, на котором мы делаем синхронизацию (чей монитор используется).
NB2: условие для вызова wait должно проверяться в while() что позволяет перепроверить условие, т.к. в редких случаях поток может проснуться без notify()
- Системная синхронизация с использованием join():
поток, внутри которого вызван join() дождется выполнения потоков на которые вызван этот join(), либо пока не пройдет время указанное в параметрах join(100 ms):psvm { // some code; otherThread.start(); otherThread.join(); // some code;}
- поток main дождется пока отработает otherThread и только потом продолжит работу. - Использование классов из пакета java.util.concurrent.Locks – механизмы
синхронизации потоков, альтернативы базовым synchronized, wait, notify, notifyAll: Lock, Condition, ReadWriteLock.
Lock lock = new ReentrantLock(); lock.lock() // lock.tryLock()=> code =>finally { lock.unlock();}
Что обозначает ключевое слово volatile? Почему операции над volatile
переменными не атомарны?
volatile запрещает копировать значение переменной в в кэш потока, т.о. она хранится только в хипе.
volatile атомарна для чтения, но не для записи и имеет смысл только когда лишь 1 поток пожет изменить значение переменной, остальные только читают
чтение(1), изменение(2) в запись(3) - другой поток может вклиниться между этими этапами.
Для чего нужны Atomic типы данных? Чем отличаются от volatile?
volatile не гарантирует атомарность. Например, операция count++ не станет атомарной просто потому, что count объявлена volatile.
C другой стороны, class AtomicInteger предоставляет атомарный метод для выполнения таких комплексных операций атомарно, например getAndIncrement() – атомарная замена оператора инкремента, его можно использовать, чтобы атомарно увеличить текущее значение на один.
Похожим образом сконструированы атомарные версии и для других типов данных.
Как устроен Increment в Atomic?
Для обеспечения целостности данных используется низкоуровневая инструкциия CAS (compare-and-swap), которая на машинном уровне объединяет 3 операции в одну атомарную.
Чем Runnable отличается от Callable?
Callable<T> как и Runnable представляет собой определенное задание, которое выполняется потоком, но
- Callable<T>: V call() throws Exception; || Runnable: void run()
- имеет return type не void
- throws Exception
- Callable параметризован: call() возвращает T или Object, если не параметризовать.
- Callable работает только с ExecutorService, R. и с ним и с Thread;</T></T>
Future<T> = содержит будущий результат работы потока, и get() на нем вызовется только когда поток завершит работу.</T>
Что такое Thread Pool и ExecutorService?
ТР - Множество потоков, каждый из которых выполняет ту или иную задачу.
Создавать лучше через Executors.newFixedThreadPool(5);
ES - создает ТР и передает в него задачи.
метод execute(runnable r) / submit(callable c) from ExService передает задачу в ТР
Что такое FutureTask? (5)
FutureTask - отменяемое асинхронное вычисление.
Этот класс предоставляет базовую реализацию Future с методами для запуска, остановки, запроса состояния и извлечения результатов вычисления.
Результат может быть получен, только когда вычисление завершено, метод получения будет заблокирован, если вычисление еще не завершено.
Объекты FutureTask могут быть использованы для обертки объектов Callable и Runnable.
Так как FutureTask помимо Future реализует Runnable, его можно передать в Executor на выполнение.
~~~
Future<Integet> future = executorService.submit(callable c);
(future.cancel() / future.get() / future.isCancelled() / future.isDone())
~~~</Integet>
Если мы передадим в submit() runnable - future будет null, однако другие методы сработают.
Что такое deadlock?
Взаимная блокировка (deadlock) – явление, при котором все потоки находятся в режиме ожидания и ничего не делают.
Может произойти, когда соблюдаются все условия:
* взаимное исключение: по крайней мере один ресурс занят в syncronized режиме, и следовательно только один поток может использовать его в конкретный момент;
* удержание и ожидание: поток уже удерживает ресурс и запрашивает дополнительные syncronized ресурсы, которые могут удерживаються другими потоками;
* цикличного ожидания: поток ждет освобождения ресурса другим потоком, который в свою очередь ждет освобождения ресурса заблокированного первым потоком.
* отсутствия предочистки: операционная система не может отбирать ресурсы у процесса;
Простейший способ избежать взаимной блокировки – не допускать цикличного ожидания. Этого можно достичь, если все потоки получают мониторы в одинаковом определенном порядке и освобождают их в обратном порядке.
Что такое livelock?
livelock – тип взаимной блокировки, при котором несколько потоков залочены навсегда, ожидают друг друга, проделывая работу буз прогресса.
Часто возникает в результате попыток предотвращения deadlock.
Узнать о наличии livelock можно, например, проверив уровень загрузки процессора в состоянии покоя.
Что такое race condition?
Состояние гонки возникает вследствии ошибки проектирования, когда несколько потоков имеют доступ к общим ресурсам и данным и порядок этого доступа хаотичен.
Допущение состояния гонки является причиной:
Data Race - ситуация, когда несколько потоков имеют доступ к переменной и хотябы один из них может ее изменить..
Lock Starvation – потоки не заблокированы, но есть нехватка ресурсов, из-за чего менее преоритетные потоки ждут либо очень долго, либо вечно.