-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.gradle/ | ||
build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
test: | ||
gradle test | ||
|
||
run: | ||
gradle run |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Примитивы синхронизации | ||
|
||
В предыдущем упражнении наши потоки работали независимо друг от друга. В реальности обычно ситуация иная. Несколько потоков должны одновременно работать с одним ресурсом и менять его. Для этого потоки должны взаимодействовать между собой, должны быть синхронизированы. Представьте ситуацию, что нескольким людям нужно обсудить некоторую проблему. Чтобы обсуждение не превратилось в хаос и люди могли слышать друг друга, одновременно должен говорить только один человек. В программировании же существует специальное понятие – мьютекс. Задача мьютекса – обеспечить, чтобы доступ к разделяемому объекту был в определённое время только у одного потока. Если Поток-1 захватил ресурс, остальные потоки не смогут получить к нему доступ и будут вынуждены ждать, пока Поток-1 не освободит ресурс | ||
|
||
## Ссылки | ||
|
||
* [Ключевое слово synchronized](https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html) | ||
* [Пример использования ключевого слова synchronized](https://www.baeldung.com/java-synchronized) | ||
|
||
## src/main/java/exercise/SafetyList.java | ||
|
||
Вам предстоит написать собственную потокобезопасную реализацию листа. Потокобезопасность этого листа заключается в том, что он может исправно функционировать при использовании его из нескольких потоков одновременно. То есть он должен обеспечивать правильный доступ нескольких потоков к разделяемым данным. | ||
Для упрощения наша реализация будет работать только с целыми числами и будет включать далеко не все методы, которые есть в интерфейсе `List`. Для реализации листа используйте массив. | ||
|
||
## Задачи | ||
|
||
* Допишите содержимое класса `SafetyList`, который представляет собой потокобезопасную реализацию листа. Добавьте в класс необходимые свойства. | ||
|
||
* Создайте публичный метод `add()`, который будет добавлять новый элемент в лист. Именно у этого метода мы должны ограничить выполнение. Сделайте так, чтобы метод мог выполняться только одним потоком одновременно. | ||
|
||
* Создайте публичный метод `get()`, который будет возвращать элемент листа по его индексу | ||
|
||
* Создайте публичный метод `getSize()`, который будет возвращать количество элементов в листе | ||
|
||
```java | ||
SafetyList list = new SafetyList(); | ||
list.add(5); | ||
list.add(7); | ||
list.get(0); // 5 | ||
list.get(1); // 7 | ||
list.getSize(); // 2 | ||
``` | ||
|
||
## src/main/java/exercise/ListThread.java | ||
|
||
В этом файле мы определим поток, который будет добавлять элементы в лист | ||
|
||
## Задачи | ||
|
||
* В файле создайте класс `ListThread`, который будет представлять собой поток. | ||
* Определите в классе конструктор, который принимает в качестве аргумента нашу реализацию листа `SafetyList` | ||
* Сделайте так, чтобы при запуске поток добавлял в переданный лист 1000 любых целых чисел. Перед добавлением каждого элемента поток должен спать в течении одной 1 миллисекунды | ||
|
||
```java | ||
// Создаём лист | ||
SafetyList list = new SafetyList(); | ||
|
||
// Создаём поток, передав туда созданный лист | ||
Thread thread = new Thread(new ListThread(list)); | ||
|
||
// Запускаем поток | ||
thread.start(); | ||
|
||
// Работает примерно 1 секунду (1000 элементов * 1 мс) | ||
|
||
// Дожидаемся его окончания | ||
thread.join(); | ||
|
||
// Поток добавил в лист 1000 элементов | ||
list.getSize(); // 1000 | ||
``` | ||
|
||
## src/main/java/exercise/App.java | ||
|
||
## Задачи | ||
|
||
* В методе `main()` создайте экземпляр потокобезопасного листа SafetyList; | ||
|
||
* Создайте и запустите два потока, каждый из которых добавит 1000 элементов в созданный лист | ||
|
||
* После окончания работы потоков выведите на экран количество элементов в листе | ||
|
||
* Запустите приложение при помощи команды `make run` и проверьте количество элементов в листе. Так как мы запустили два потока, каждый из которых добавляет 1000 элементов, итоговое количество элементов должно быть равно 2000. | ||
|
||
* Попробуйте убрать синхронизацию из метода `add()` нашего листа. Прежде чем запустить программу, попробуйте ответить на вопрос: сколько элементов будет в листе после окончания работы потоков? | ||
|
||
* Запустите приложение несколько раз и изучите получившееся количество элементов. Количество элементов в листе в этом случае нельзя предугадать заранее, но практически наверняка каждый раз оно будет меньше 2000. Как думаете, почему так происходит? | ||
|
||
* Перед запуском тестов не забудьте вернуть синхронизацию в метод `add()` нашей реализации листа | ||
|
||
### Подсказки | ||
|
||
* При реализации метода `add()` листа, вам может пригодиться эта [статья](https://habr.com/ru/post/128269/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
plugins { | ||
id 'application' | ||
id 'com.adarshr.test-logger' version '2.1.1' | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.0-M1' | ||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.0-M1' | ||
testImplementation 'org.assertj:assertj-core:3.19.0' | ||
testImplementation 'com.google.code.tempus-fugit:tempus-fugit:1.1' | ||
} | ||
|
||
application { | ||
mainClass = 'exercise.App' | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
} | ||
|
||
// testlogger { | ||
// showStandardStreams = true | ||
// } | ||
|
||
tasks.withType(JavaCompile) { | ||
options.encoding = "UTF-8" | ||
} |
64 changes: 64 additions & 0 deletions
64
java-advanced-ru/sync-primitives/examples/ThreadExample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Разделяемый ресурс | ||
public class CommonResource { | ||
private int counter = 0; | ||
|
||
// Метод, у котрого нужно ограничить выполнение | ||
// Одновременно только один поток может выполнять метод increaseCounter() | ||
public synchronized void increaseCounter() { | ||
counter++; | ||
} | ||
|
||
public int getCounter() { | ||
return counter; | ||
} | ||
} | ||
|
||
// Создаё класс потока | ||
public class ThreadExample extends Thread { | ||
|
||
// Разделяемый ресурс | ||
CommonResource resource; | ||
|
||
ThreadExample(CommonResource resource) { | ||
this.resource = resource; | ||
} | ||
|
||
// Метод увеличивает счетчик на 1 | ||
@Override | ||
public void run() { | ||
resource.increaseCounter(); | ||
} | ||
} | ||
|
||
public class Example { | ||
|
||
public static void main(String[] args) { | ||
|
||
CommonResource resource = new CommonResource(); | ||
|
||
// Создадим два потока, которые будут менять разделяемый ресурс | ||
ThreadExample thread1 = new ThreadExample(resource); | ||
ThreadExample thread2 = new ThreadExample(resource); | ||
|
||
// Запускаем потоки | ||
thread1.start(); | ||
thread2.start(); | ||
|
||
// Дожидаемся окончания выполнения потоков | ||
try { | ||
|
||
thread1.join(); | ||
thread2.join(); | ||
|
||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
// Проверяем результат | ||
System.out.println("Size: " + resource.getCounter()); | ||
} | ||
} | ||
|
||
// Вывод в консоль | ||
|
||
// => "Size: 2" |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rootProject.name = 'sync-primitives' |
11 changes: 11 additions & 0 deletions
11
java-advanced-ru/sync-primitives/src/main/java/exercise/App.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package exercise; | ||
|
||
class App { | ||
|
||
public static void main(String[] args) { | ||
// BEGIN | ||
|
||
// END | ||
} | ||
} | ||
|
5 changes: 5 additions & 0 deletions
5
java-advanced-ru/sync-primitives/src/main/java/exercise/ListThread.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package exercise; | ||
|
||
// BEGIN | ||
|
||
// END |
7 changes: 7 additions & 0 deletions
7
java-advanced-ru/sync-primitives/src/main/java/exercise/SafetyList.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package exercise; | ||
|
||
class SafetyList { | ||
// BEGIN | ||
|
||
// END | ||
} |
27 changes: 27 additions & 0 deletions
27
java-advanced-ru/sync-primitives/src/test/java/exercise/AppTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package exercise; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.RepeatedTest; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
class AppTest { | ||
private static SafetyList list; | ||
|
||
@BeforeEach | ||
void beforeAll() { | ||
list = new SafetyList(); | ||
} | ||
|
||
@RepeatedTest(value = 3) | ||
void testThreadSafetys() throws InterruptedException { | ||
|
||
Thread thread1 = new Thread(new ListThread(list)); | ||
Thread thread2 = new Thread(new ListThread(list)); | ||
thread1.start(); | ||
thread2.start(); | ||
thread1.join(); | ||
thread2.join(); | ||
|
||
assertThat(list.getSize()).isEqualTo(2000); | ||
} | ||
} |