JavaRush/Java блог/Архив info.javarush/Немного вопросов по нитям с собственными примерами.
Pegas
34 уровень

Немного вопросов по нитям с собственными примерами.

Статья из группы Архив info.javarush
участников
Создал класс SomeThread implements Runnable В нем один метод incrementString(); добавляющий букву к строке и выводящий строки на консоль. Метод вызывается через run(); Если incrementString(); пометить synchronized, то вывод стабильно такой: a-b Thread-1 aa-bb Thread-0 А если без synchronized (как в коде ниже), то выводит, кроме прочих вариантов и так: a-b Thread-1 a-b Thread-0 Не могу понять почему так происходит. Получается, что одна из нитей не выполняет операцию s1 += или s2 += Кто поможет разобраться в этом конкретном примере? Заранее спасибо) public class TestThread { public static void main(String[] args) { SomeThread someThread = new SomeThread(); Thread thread1 = new Thread(someThread); Thread thread2 = new Thread(someThread); thread1.start(); thread2.start(); } } class SomeThread implements Runnable { String s1 = ""; String s2 = ""; @Override public void run() { try { incrementString(); } catch (IOException e) { e.printStackTrace(); } } void incrementString() throws IOException { s1 += "a"; s2 += "b"; System.out.println(s1+ "-" + s2 + " " + Thread.currentThread().getName()); } }
Комментарии (9)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Pegas
Уровень 34
21 мая 2016, 18:46
Попытался детально разобраться с поднятым мной в этой теме вопросом. Что же я понял? И так. Во-первых, volatile(V) является как-бы урезанной версией synchronized(S). При попытке изменить поле, помеченное V, происходит копирование переменной в кеш, ее обработка и обратное копирование в оперативную память. При этом ни одна нить не сможет получить доступ к переменной, помеченной V, если с ней уже работает какая-либо нить (не сможет ее поменять либо взять ее значение). Пометка V дает возможность получить самое актуальное значение поля для каждой нити. Что же касается S, то он блокирует доступ не только к переменной, но и к объекту в момент исполнения метода этого объекта, помеченного S. S в моем понимании дает более широкие возможности по сравнению с V в том случае, если в блоке кода нужно, к примеру, модифицировать поле, а потом в этом же блоке и воспользоваться именно ЭТИМ значением поля, а не другим, которое может присвоить ЭТОМУ полю другая нить. Ведь в случае с V один метод 1-ой нити может изменить поле, а во второй метод, в котором нужно значение, выданное первым методом, может придти уже измененное значение, которое может изменить вторая нить. S-блок этого не допустит.
Жду комментов от бывалых. Спасибо за прочтение)
Pegas
Уровень 34
20 мая 2016, 20:37
Вот интересные и емкие изречения по поводу synchronized и volatile, кому интересно:
— у переменной есть мастер копия плюс по копии на каждую нить, которые её используют. Мастер копия синхронизируется с локальной копией нити при входе/выходе в/из блока synchronized. Иногда, например, пустой блок synchronized(lock){} имеет смысл.
у переменных с модификатором volatile локальных копий нет. Все нити работают с мастер копией.

В связи с этим я задумался на одним моментом??? Зачем нужен модификатор volatile, если synchronized обеспечивает постоянное взаимодействие с мастер-копией переменной и поддержанием ее в актуальном состоянии. К тому же в момент выполнения блока synchronized переменную никто не сможет поменять, так как объект будет заблокирован. Если только такой случай, если модифицируемое поле находится за пределами объекта??
blacky
Уровень 23
20 мая 2016, 23:07
чтение/запись поля типа long на 32х битной архитектуре
Поддерживать мастер-копию в актуальном состоянии при помощи synchronized для всех нитей очень накладно. Пускай у нас есть http-сервер, к которому обращаются клиенты за услугами, а он в свою очередь обращается к общей базе (или файлу) и доступ к ней синхронизирован (т.е. доступ имеет только 1 в единицу времени), получаем следующее: если у одного из клиентов dial-up соединение, то ответы-запросы от него могут занимать длительное время, а остальные клиенты будут долго-долго ждать. Этот bottle-neck исправили happens-before поведением, т.е. описаны моменты, когда память нитей обновляется (см. JSR-133 FAQ). Скинь пжлст ссылку, где говорится о мастер копиях.
Pegas
Уровень 34
21 мая 2016, 08:30
Спасибо, полезные ссылки))
blacky
Уровень 23
19 мая 2016, 22:45
А какой результат ты хотел получить?
volatile решит проблему видимости shared значения для нитей, но не решит проблему атомарности операций. Если одна нить читает, а вторая пишет, то для общедоступного ресурса идеален volatile. Но если два потока считывает и пишет, то ресурс без синхронизации, даже с volatile, будет в неопределенном состоянии. см. "Example 8.3.1.4-1. volatile Fields"
Docktor91
Уровень 40
19 мая 2016, 22:11
может из-за этого?
volatile String s1 = "";
    volatile String s2 = "";
Pegas
Уровень 34
19 мая 2016, 22:17
Получается, что переменные не успевают записаться в основную память. А то, что записано в кеш первой нитью, «затирается» второй??
Docktor91
Уровень 40
19 мая 2016, 22:20
ну тип того))
получается в кеше пустая строка и когда
s1 += "a";

получается
s1=""+"a"
Pegas
Уровень 34
20 мая 2016, 12:19
Хм, кажется понял) Получается, что первый поток считает s1="" и второй считывает s1="", так как первый еще не успел модифицировать и пересохранить считанную переменную. В результате первый поток сохранит свой результат работы в переменную, а потом второй сделает тоже самое — сохранит в эту же переменную результат своей работы, но уже поверх старого значения.