Skip to content

Commit

Permalink
Download sync primitives homework
Browse files Browse the repository at this point in the history
  • Loading branch information
JackKaif committed Feb 21, 2024
1 parent d8feb88 commit 802c4b9
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 0 deletions.
2 changes: 2 additions & 0 deletions java-advanced-ru/sync-primitives/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gradle/
build/
5 changes: 5 additions & 0 deletions java-advanced-ru/sync-primitives/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
test:
gradle test

run:
gradle run
112 changes: 112 additions & 0 deletions java-advanced-ru/sync-primitives/README.html

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions java-advanced-ru/sync-primitives/README.md
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/)
31 changes: 31 additions & 0 deletions java-advanced-ru/sync-primitives/build.gradle
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 java-advanced-ru/sync-primitives/examples/ThreadExample.java
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 added java-advanced-ru/sync-primitives/presentation.pdf
Binary file not shown.
1 change: 1 addition & 0 deletions java-advanced-ru/sync-primitives/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'sync-primitives'
11 changes: 11 additions & 0 deletions java-advanced-ru/sync-primitives/src/main/java/exercise/App.java
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
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package exercise;

// BEGIN

// END
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package exercise;

class SafetyList {
// BEGIN

// END
}
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);
}
}

0 comments on commit 802c4b9

Please sign in to comment.