Лабораторная работа №1: Создание и синхронизация потоков — КиберПедия 

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

Лабораторная работа №1: Создание и синхронизация потоков

2022-10-10 96
Лабораторная работа №1: Создание и синхронизация потоков 0.00 из 5.00 0 оценок
Заказать работу

Задание:
Упражнение 1 (Синхронизация потоков). - Откройте проект SyncMainв каталоге Лабы Java2/Threads/SyncMain. - Запустите его, обратите внимание, что в программе создаётся два дополнительных потока. Каждый из них увеличивает счётчик 10 раз, а окончательный результат равен 10, вместо 20. - Добавьте необходимый код для синхронизации счётчика «count» из класса «Data». Весь код синхронизации должен быть в методе «Add». - Добавьте необходимый код для синхронизации статического счётчика «countSt» из класса «Data». Весь код синхронизации должен быть в методе «AddStatic». - Запустите программу, оба счётчика равны 20? Почему?

 

Значит, у нас есть некий волшебный объект класс Data в котором лежит два счетчика. Я специально не заморачивался гетерами, сетерами, потому что примерно для нас выглядит все одинаково. У меня есть счётчики. Чтобыпрочувствоватьразницуодинобычный, другойstatic. Идем дальше… Создается класс, который реализует интерфейс Runnable. Ничего сложного, мы это сегодня разбирали. Смотримреализациюметодаrunвэтомклассе. Он 10 раз вызывает метод Add()и 10 раз вызывает метод AddStatic(). Соответственно, идем теперь обратно наверх и смотрим, что делает метод Add. Addувеличивает счетчик обычной переменной, а staticстатической. Не надо здесь пытаться улучшать, что написано. Здесь специально сделано в блоке tryтак, чтобы было гарантирована конфликтная ситуация. Один прочитал и спит, а в это время другой меняет, а потом он продолжает. И здесь гарантируют 100%, что всегда одновременно меняют. Поэтому внутри tryне надо ничего менять.

А теперь смотрим, кто меняет данные, и сколько этих потоков? Это естественно, часть main. В mainсоздается один объект класса Data. И он кидается в два потока. т.е. объект раздали двум потокам. И каждый поток в run10 раз увеличивает счетчик. Скажите, если два потока каждый по 10 раз увеличивает счетчик, то он должен с 0 прийти куда? к 20. Мы здесь специально долго ждем, чтобы они все успели доделать, и печатаем счетчики. Долго ждем, чтобы все успели, т.к. там много sleep. Исчётчикиравны 10, вместо 20. Почему? Потому что там была гарантированно создана ситуация этого самого конфликта, когда у нас одновременно происходит наложение увеличений. Один прочитал, а другой изменил и он изменил. Ваша задача: подумать, как здесь сделать синхронизацию.

run: 10 10 СБОРКА УСПЕШНО ЗАВЕРШЕНА (общее время: 3 секунды)

 

Решение:

run: 20 20 СБОРКА УСПЕШНО ЗАВЕРШЕНА (общее время: 3 секунды)

 

Разбор лабораторной работы №1:

Все написали на лабораторной работе вот так.

Это неправильный вариант. Если мы так поступим и нажмем клавишу F6, то увидим:

run: 10 20 СБОРКА УСПЕШНО ЗАВЕРШЕНА (общее время: 3 секунды)

И вроде как мы обсуждали, что 20 – это хороший вариант, а 10 не очень. Но я вас должен расстроить. Если написать вот так, то в обоих случаях результат некорректный. Даже вторая «двадцатка» - это профанация. Просто нам не повезло и результат совпал с правильным. А в жизни сделаете пару движений и опять будет проблема с данными и безопасностью данных при одновременном доступе их разных потоков.

Но начнем с 1-го synchronized:

Смотрим на экран и пытаемся ответить на один вопрос. synchronized защищает данные объекта. Какого именно объекта? obj. synchronizedнаписан перед методом. Туда попадет когда-нибудь первый поток? Попадет. И на каком объекте включена защита, что второй поток не может попасть в этот раздел synchronized?

Это же метод! Член класса! Что он получает на вход? this! Это синхронизация на this! Понимаете, слово synchronizedк объекту относится и мы говорим «вот этот объект защити, пожалуйста, от одновременного доступа – this

Давайте нарисуем картинку, и потом станет понятно, почему ничего не работает.

Мы создали объект Data. В этом объекте сидит счетчик «counter». staticcounterя не могу написать, т.к. вы понимаете, что он не к объекту относится. Далее, у меня в системе создается два объекта класса MyThread1, MyThread2. Сделали 2 объекта. Каждому из них, когда создается программа, ему ссылочку на counterкидают. Затем, вызывается startу одного потока и у другого. Оба потока стартуют и начинают вызывать метод Add. Смотрите, допустим у нас там два вспомогательных потока. Первый поток запустился. Предположим, что первый поток связан с первым объектом. Поток сюда пришел в метод Add, где написано «synchronized void Add()». Поток этот вызывает метод и проверяет «объект занят? кто-то вошел в этот объект в этом разделе synchronized? Нет.».

Теперь, у нас любой класс наследует от Object. В Object есть специальное поле (член класса), который и отвечает за этот признак «объект занят или свободен, используется или наоборот его еще никто не задействовал». И раз этот первый объект в раздел synchronized заходит, то synchronized в этом объекте ставит «Занято!». И это работает. И если кто-то в этот объект попытается зайти, то synchronized скажет «Занято!». И другой поток будет ждать. Но когда наш второй поток запустится и снова произойдет вызов метода Add, то он будет на каком объекте метод Add?На втором. Конечно! Поток придет к тому же вроде методу Add, но на другом объекте. И synchronized проверит «Свободно? Да, свободно.». И второй поток сможет совершенно спокойно начать этот метод Addиспользовать. И также будет сказано «Занято!».

А теперь посмотрите, что они в этом методе Addделают. Они одновременно обращаются к одному объекту Data. Кого надо было защищать? Надо было защищать count. Он же тоже от Objectнаследует. Надо было тут галочку ставить, что занято. И тогда бы они работали с ним по-одному.

Й synchronized:

synchronizedнаписан? да. Защиту от одновременного доступа включать мы просим? да. А на каком объекте? synchronized написан перед методом AddStatic. Хорошо, что вы thisне говорите, зная, что staticне получает this. Но вопрос-то остается ребром. Защита именно на объекте, а на каком? На Data? А кто тут такой умный в этой программе? Кто ее выбрал? Здесь не понятно, откуда может взяться Data. Почему он должен был выбрать произвольно строчку и сказать «О! Давайте на этой штуке будет. Или, а давайте на другой!». Нет! Это не принимается! У нас на руках пара потоков и Data, но пока ни один не подходит. А вы не все назвали объекты. Вы скрыли еще пару от меня. Ответ: Синхронизация происходит на объекте не экземпляра класса, а на самом объекте класса. Помните рефлексию (метаданные класса)? Мы говорим, «дай мне описание класса!». И вам возвращают объект какого класса? Класс. Вот на нем! На описании объекта «класс».

Когда виртуальная машина начинает работать, прежде чем она начинает с любым типом что-то делать, она загружает его описание. Это то, что является у нас рефлексией. У нас есть описание класса «Data». В нем находится staticintcountSt. У нас есть описание объекта MyThread. Класс один, а он Singleton. Ведь объект описание, он класс-одиночка. А он (этот класс) в свою очередь, должен наследовать от Object.Куда ему деваться-то? Поэтому во всех сценариях и здесь, и здесь у нас где-то присутствует внутри унаследованный Object, где есть возможность этот флажок «занято-свободно» переключать. У нас вызывается два потока, запускается поток, он вызывает статический метод. Вот пришел первый поток. Первому потоку повезло и он вызывает synchronizedstatic-метод. Проверяет этот объект «ты свободен?», а ему отвечают «да, свободен». Отлично, и он отсюда куда обращается? К другому объекту.

Второй поток запускается. Он на каком объекте будет проверять «занято-свободно»? На том же! Метод один и тот же. Из одного и того же класса MyThread. Поэтому второй поток уперся в synchronized, а synchronized смотрит и говорит «а объект класса MyThreadзанят! Жди дружок!». Ну он ждет. И у вас получается, что они по очереди работают с данными? По очереди. Но согласитесь, это было бы достаточно странно, что идя к ресурсу, который вам очень нужен на перерыве, вы в соседнюю дверь ломитесь. Вам нужен ресурс, вы зачем-то слева дверь открываете и идя к ресурсу, вы закрываете левую дверь. А тот, кто идет следующий, тоже почему-то идет не к ресурсу, а к левой двери. И вдвоем каждый левую дверь дергает, а потом идет вправо к ресурсу. Может же попасться нормальный человек, который пойдет сразу к ресурсу. Нам надо делать блокировку на объекте класса Data. А когда вы ставите synchronized перед статическим методом, то за вас выбрали, что это объект класса MyThread–это не то. Т.е. если разжевать все более подробно, то там был MyThread.classвместо Data.class.

Будьте внимательнее! У вас ООП! Если вы пишете synchronized, то непонятно на каком объекте надо разбираться, иначе вы такого поназащищаете.

Кстати, а что будет если вы везде понаставите synchronized, а данные у вас будут публичными? Может кто-то напрямую к ним обратиться? Вы сможете его уговорить всегда писать synchronized? Это будет не потокобезопасный код. Понятно, да? Как только вы нарушаете инкапсуляцию, гарантий что кто-то в обход ваших гетеров и сетеров пойдет нет. Кто-то возьмет и напишет напрямую count = 7. И всё! Никакой потокобезопасности. Поэтому данные приватные. И вы понимаете, если у вас в классе есть какое-то поле с данными, которые в 10 методах используются, чтобы с ним проводить какие-то вычисления, то вы должны synchronizedпоставить где? Во всех 10 методах. Потому что если в 9 поставить, а в одном нет, то это опять не потокобезопасный код. Потому что где-то в большой и серьезной системе обязательно одновременно произойдёт срабатывание. К чему мы придем? Представьте себе класс с важными данными (коллекции, например). И все методы класса с этой коллекцией работает. Кто-то меняет, кто-то добавляет, кто-то убавляет. Даже тот, кто ищет, должен блокировать, потому что если он ищет, а в этот момент кто-то удалит, то я вообще не знаю, к чему это все может привести. Поэтому у вас получится что в классе 10, 20, 30 методов и все они synchronized.


Поделиться с друзьями:

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Эмиссия газов от очистных сооружений канализации: В последние годы внимание мирового сообщества сосредоточено на экологических проблемах...

Адаптации растений и животных к жизни в горах: Большое значение для жизни организмов в горах имеют степень расчленения, крутизна и экспозиционные различия склонов...

Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.014 с.