Синхронизация в Java

Асинхронный вызов

Синхронный неблокируемый вызов

Синхронный блокирующий вызов

Синхронизация внутри одного процесса

Открытие функции – это всегда какое-то время ожидания. Прикладной программист не думает о том, сколько отрабатывает функция. Системный программист знает, что есть время ожидания и она может работать долго.

Это тогда, когда функция выполняет активную часть и уходит в ожидание, потом продолжает.

Функция выполняет активную фазу, понимает, что действия, которые я хочу выполнить далее недоступны и возвращает код ошибки и время ожидания становится минимальным.

0, 0, IPC_NONBLOCK – после этого ключа операция semop() работает следующим образом: можно совершить действие – совершает, нельзя – сразу идет отказ. Если ключа нет, то будет ожидать всегда.

Вызываю функцию, у нее проходит активная фаза, после этого говорится «Принято к сведению» и я продолжаю, но и функция продолжает и когда функция выполнена, она сообщает об этом.

Некоторые действия можно вызывать в асинхронном режиме.

-Как сделать так, чтобы синхронизация работала безопасно?

-Не давать программисту пользоваться синхронизацией!

Монитор – автоматизированная система, которая интегрируется в программу компилятором и является аналогом сборщика мусора (автоматическая система, которая отвечает за синхронизация).

Монитор строго завязан на язык программирования (не на функции). Функции человек может забыть написать. В самом языке программирования появляются операторы синхронизации.

Volatile()

synchronized()

 

Object o Integer N


 

synchronized(o){
while(N != 0);
0 <– запись
N=1;
}

synchronized(o){
while(N != 1);
0 –> читать
N=0;
}

Для нас N – это ресурс, который нам даст кто-то другой. Доступ к нему засинхронизирован. N может быть отдельной переменной (volatile), либо полем. В результате получается взаимная блокировка.

Как решить?
Окружить это все блоком и делать здесь прерывание

while(true){
synchronized(o){
if(N != 0)continue;
0 <– запись
N=1;
break;
}
}

while(true){
synchronized(o){
if(N != 1)continue;
0 –> читать
N=0;
break;
}
}

В результате эти две задачи уже не являются тупиком в теории. Теоретически, задача, которая работает, зашла, засинхронизировалась, вышла. Вторая задача то синхронизируется, то нет (мигалка). В теории этот объект может быть свободен и захвачен. В Java-машине не заложено, какой вид параллелизма используется. Мы ставим искусственную задержку (sleep), что понижает вероятность захвата объекта на этапе синхронизации.

Рисунок


 

Я могу средствами Java вызывать метод wait()

1 synchronized(o){
2 while(N != 0)o.wait();
0 <– запись
N=1;
}

Здесь я перевожу себя в спящий режим и синхронизацию над объектом отпускаю.

8 synchronized(o){
9 while(N != 1)o.wait();
10 0 –> чтение
11 N=0;
12 o.notify();
13 }

Поток переходит в спящий режим, освобождается ситуация о. Представьте, что буфер занят, писателю работать нельзя, читателей нет. Он засинхронизировался, ушел в режим ожидания. Читатель срабатывает в квант времени 8, но писатель спит, значит он отпустил объект синхронизации. Мы всё выполнили и тот не проснулся. Может возникнуть ситуация, когда одни объекты засыпают и не просыпаются. Поэтому нужно им разрешить захватить объект. Для этого используется метод notify(), который будит все объекты, которые ждут объекта. o.notify() отрабатывает так: если один объект (читатель) вызвал notify(), то он автоматически будит объекты которые ждут его. Но в один критический момент может быть активным только один объекти и монитор будет следить за этим.После notify() объект проснется, но не отработает, пока не завершится критическая секция и объект о будет отпущен. Тогда монитор разрешит писателю отработать под тактом 14

1 synchronized(o){
2 while(N != 0)o.wait();
0 <– запись
N=1;
!!!! o.notify()
}

Под тактом 14 будет произведен выход из synchronized(), затем после освобождения объекта произойдёт вход в synchronized().

wait() и notify() увеличивают эффективность, но снижают надёжность, поэтому нужно его писать не только в читателе, но и в писатале!!

Способы нестандартного вызова notify():

1. notify() можно вызвать не только в пределах критических секций, но и снаружи. Он посылает всем потомкам проснуться для того, чтобы продолжить вход в критическую секцию и заставить работать.

2. можно вызвать wait() тоже вне критической секции.
Есть некоторая переменная
volatile int n = 0;
while(n!=0) n = 1; x.wait()

3. o.notify() будит одного (представьте, что писателей 10, они заходят в критическую секцию и засыпают, затем будится 1 поток, случайный)
o.notifyAll() – разбудить всех спящих, просыпаются и работают по своим алгоритмам.

Синхронизация, встроенная в Java происходит на мониторе, он один на машину. Если нужно запустить две Java-программы, то запускаем две разные Java-машины, у каждой из которых свой монитор и эти программы засинхронизировать нельзя.

Разница между многопоточным программированием и многопроцессным:

У каждого процесса свои ресурсы и блоки памяти, они не могут взаимодействовать друг с другом никак, кроме через средства ОС. Если у нас программирование многопоточное, то у них общие ресурсы и общая память, каждая ячейка памяти доступна любому потоку. Многопоточное программирование не зависит от ОС, это уже автоматически общие ресурсы и общая память, это плюс.

Но также есть минусы:

· ОС не знает, что у меня внутри есть потоки, поэтому многопоточность я должен реализовать сам, и один процесс может исполняться только на одном процессоре.

· Если один поток закрывает процесс, то закрывается процесс
Если данные изменяются, то изменяются во всех потоках

Если нам нужно организовать программный комплекс из нескольких процессов, то только средствами ОС.

Многопоточность может быть реализована языком программирования и тогда ОС считает это всё монолитным комплексом. Изначально потоки в Java были без API. Затем их добавили. Теперь можно создать псевдопроцесс, который берет ресурсы и память из процесса родителя, только стек у него свой собственный.

Ресурсы
Процесс Процесс

Ресурсы, память
Код
стек
данные



В Java в некоторых Java-машинах есть потоки, которые выглядят, как процесс, есть потоки, которые выглядят, как процессопотоки. Из некоторых JVM можно запустить С кусок кода, а следовательно, можно вызвать семафоры Дейкстры, если потоки с точки зрения ОС как процессопотоки. В Solaris Java могла напрямую писать в память, использовать любые API. В некоторых ОС писать на Java очень удобно.