diff --git a/src/main/java/edu/hw7/task1/MultiCounter.java b/src/main/java/edu/hw7/task1/MultiCounter.java new file mode 100644 index 0000000..a11f549 --- /dev/null +++ b/src/main/java/edu/hw7/task1/MultiCounter.java @@ -0,0 +1,20 @@ +package edu.hw7.task1; + +import java.util.concurrent.atomic.AtomicInteger; + +public class MultiCounter { + + private final AtomicInteger counter; + + public MultiCounter() { + counter = new AtomicInteger(0); + } + + public void increment() { + counter.incrementAndGet(); + } + + public int get() { + return counter.get(); + } +} diff --git a/src/main/java/edu/hw7/task2/ParallelFactorial.java b/src/main/java/edu/hw7/task2/ParallelFactorial.java new file mode 100644 index 0000000..3bef776 --- /dev/null +++ b/src/main/java/edu/hw7/task2/ParallelFactorial.java @@ -0,0 +1,16 @@ +package edu.hw7.task2; + +import java.util.stream.IntStream; + +public final class ParallelFactorial { + + private ParallelFactorial() { + } + + public static int computeFactorial(int n) { + return IntStream.rangeClosed(1, n) + .parallel() + .reduce((i1, i2) -> i1 * i2) + .orElseThrow(); + } +} diff --git a/src/main/java/edu/hw7/task3/AbstractPersonDatabase.java b/src/main/java/edu/hw7/task3/AbstractPersonDatabase.java new file mode 100644 index 0000000..7b95c31 --- /dev/null +++ b/src/main/java/edu/hw7/task3/AbstractPersonDatabase.java @@ -0,0 +1,64 @@ +package edu.hw7.task3; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; + +public class AbstractPersonDatabase implements PersonDatabase { + + private final Map cachedIds = new HashMap<>(); + private final Map> cachedNames = new HashMap<>(); + private final Map> cachedAddresses = new HashMap<>(); + private final Map> cachedPhones = new HashMap<>(); + + @SneakyThrows + @Override + public void add(Person person) { + cachedIds.put(person.id(), person); + cachedNames.computeIfAbsent(person.name(), name -> new ArrayList<>()).add(person); + cachedAddresses.computeIfAbsent(person.address(), name -> new ArrayList<>()).add(person); + cachedPhones.computeIfAbsent(person.phoneNumber(), name -> new ArrayList<>()).add(person); + } + + @Override + public void delete(int id) { + Person removed = cachedIds.remove(id); + if (removed == null) { + return; + } + cachedNames.remove(removed.name()); + cachedAddresses.remove(removed.address()); + cachedPhones.remove(removed.phoneNumber()); + } + + @Override + @Nullable + public List findByName(String name) { + if (!cachedNames.containsKey(name)) { + return null; + } + return cachedNames.get(name).stream().toList(); + } + + @Override + @Nullable + public List findByAddress(String address) { + if (!cachedAddresses.containsKey(address)) { + return null; + } + return cachedAddresses.get(address).stream().toList(); + } + + @Override + @Nullable + public List findByPhone(String phone) { + if (!cachedPhones.containsKey(phone)) { + return null; + } + return cachedPhones.get(phone).stream().toList(); + } + +} diff --git a/src/main/java/edu/hw7/task3/CachingPersonDatabase.java b/src/main/java/edu/hw7/task3/CachingPersonDatabase.java new file mode 100644 index 0000000..260d75d --- /dev/null +++ b/src/main/java/edu/hw7/task3/CachingPersonDatabase.java @@ -0,0 +1,37 @@ +package edu.hw7.task3; + +import java.util.List; +import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; + +public class CachingPersonDatabase extends AbstractPersonDatabase { + + @SneakyThrows + @Override + public synchronized void add(Person person) { + super.add(person); + } + + @Override + public synchronized void delete(int id) { + super.delete(id); + } + + @Override + @Nullable + public synchronized List findByName(String name) { + return super.findByName(name); + } + + @Override + @Nullable + public synchronized List findByAddress(String address) { + return super.findByAddress(address); + } + + @Override + @Nullable + public synchronized List findByPhone(String phone) { + return super.findByPhone(phone); + } +} diff --git a/src/main/java/edu/hw7/task3/LockCachingPersonDatabase.java b/src/main/java/edu/hw7/task3/LockCachingPersonDatabase.java new file mode 100644 index 0000000..e81786a --- /dev/null +++ b/src/main/java/edu/hw7/task3/LockCachingPersonDatabase.java @@ -0,0 +1,66 @@ +package edu.hw7.task3; + +import java.util.List; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; + +public class LockCachingPersonDatabase extends AbstractPersonDatabase { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + @SneakyThrows + @Override + public void add(Person person) { + try { + lock.writeLock().lock(); + super.add(person); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void delete(int id) { + try { + lock.writeLock().lock(); + super.delete(id); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + @Nullable + public List findByName(String name) { + try { + lock.readLock().lock(); + return super.findByName(name); + } finally { + lock.readLock().unlock(); + } + } + + @Override + @Nullable + public List findByAddress(String address) { + try { + lock.readLock().lock(); + return super.findByAddress(address); + } finally { + lock.readLock().unlock(); + } + } + + @Override + @Nullable + public List findByPhone(String phone) { + try { + lock.readLock().lock(); + return super.findByPhone(phone); + } finally { + lock.readLock().unlock(); + } + } +} diff --git a/src/main/java/edu/hw7/task3/Person.java b/src/main/java/edu/hw7/task3/Person.java new file mode 100644 index 0000000..d8ab7d0 --- /dev/null +++ b/src/main/java/edu/hw7/task3/Person.java @@ -0,0 +1,4 @@ +package edu.hw7.task3; + +public record Person(int id, String name, String address, String phoneNumber) { +} diff --git a/src/main/java/edu/hw7/task3/PersonDatabase.java b/src/main/java/edu/hw7/task3/PersonDatabase.java new file mode 100644 index 0000000..330e2ca --- /dev/null +++ b/src/main/java/edu/hw7/task3/PersonDatabase.java @@ -0,0 +1,18 @@ +package edu.hw7.task3; + +import java.util.List; +import org.jetbrains.annotations.Nullable; + +public interface PersonDatabase { + void add(Person person); + + void delete(int id); + + @Nullable List findByName(String name); + + @Nullable + List findByAddress(String address); + + @Nullable + List findByPhone(String phone); +} diff --git a/src/main/java/edu/hw7/task4/MonteCarloPiComputer.java b/src/main/java/edu/hw7/task4/MonteCarloPiComputer.java new file mode 100644 index 0000000..7020ee8 --- /dev/null +++ b/src/main/java/edu/hw7/task4/MonteCarloPiComputer.java @@ -0,0 +1,60 @@ +package edu.hw7.task4; + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadLocalRandom; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class MonteCarloPiComputer { + + private static final double MONTE_CARLO_CONST = 4.0; + private static final double RADIUS = 0.5; + + public static double computePI(long iterationsCount) { + double result = countRandomCollisions(iterationsCount); + return MONTE_CARLO_CONST * (result / iterationsCount); + } + + @SneakyThrows + public static double computePIParallel(long n, int threadCount) { + var executorService = Executors.newFixedThreadPool(threadCount); + double count = 0; + + // Not using atomics here to speed up the process + Future[] futures = new Future[threadCount]; + CountDownLatch countDownLatch = new CountDownLatch(threadCount); + for (int thread = 0; thread < threadCount; thread++) { + futures[thread] = executorService.submit(() -> { + long countInThread = countRandomCollisions(n / threadCount); + countDownLatch.countDown(); + return countInThread; + }); + } + countDownLatch.await(); + for (int i = 0; i < threadCount; i++) { + count += futures[i].get(); + } + return MONTE_CARLO_CONST * (count / n); + } + + private long countRandomCollisions(long iterationsCount) { + long count = 0; + Random random = ThreadLocalRandom.current(); + for (long i = 0; i < iterationsCount; i++) { + double x = random.nextDouble(); + double y = random.nextDouble(); + if (isInSingleCircle(x, y)) { + count++; + } + } + return count; + } + + private static boolean isInSingleCircle(double x, double y) { + return (x - RADIUS) * (x - RADIUS) + (y - RADIUS) * (y - RADIUS) <= RADIUS * RADIUS; + } +} diff --git a/src/test/java/edu/hw7/task1/MultiCounterTest.java b/src/test/java/edu/hw7/task1/MultiCounterTest.java new file mode 100644 index 0000000..a343031 --- /dev/null +++ b/src/test/java/edu/hw7/task1/MultiCounterTest.java @@ -0,0 +1,31 @@ +package edu.hw7.task1; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import lombok.SneakyThrows; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class MultiCounterTest { + + @SneakyThrows + @Test + @DisplayName("Тестирование MultiCounter#get и MultiCounter#increment") + public void get_shouldReturnCorrectInt_whenIncremented() { + final int threadCount = 5; + var executorService = Executors.newFixedThreadPool(threadCount); + var countDownLatch = new CountDownLatch(threadCount); + var counter = new MultiCounter(); + for (int i = 0; i < threadCount; i++) { + executorService.submit(() -> { + for (int j = 0; j < 1000; j++) { + counter.increment(); + } + countDownLatch.countDown(); + }); + } + countDownLatch.await(); + Assertions.assertThat(counter.get()).isEqualTo(threadCount * 1000); + } +} diff --git a/src/test/java/edu/hw7/task2/ParallelFactorialTest.java b/src/test/java/edu/hw7/task2/ParallelFactorialTest.java new file mode 100644 index 0000000..682a267 --- /dev/null +++ b/src/test/java/edu/hw7/task2/ParallelFactorialTest.java @@ -0,0 +1,14 @@ +package edu.hw7.task2; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ParallelFactorialTest { + + @Test + @DisplayName("Тестирование ParallelFactorial#computeFactorial") + public void computeFactorial_shouldReturnCorrectInt() { + Assertions.assertThat(ParallelFactorial.computeFactorial(5)).isEqualTo(120); + } +} diff --git a/src/test/java/edu/hw7/task3/PersonDatabaseTest.java b/src/test/java/edu/hw7/task3/PersonDatabaseTest.java new file mode 100644 index 0000000..6a0bb47 --- /dev/null +++ b/src/test/java/edu/hw7/task3/PersonDatabaseTest.java @@ -0,0 +1,52 @@ +package edu.hw7.task3; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import lombok.SneakyThrows; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class PersonDatabaseTest { + + @Test + @DisplayName("Тестирование LockCachingPersonDatabase") + public void lockDatabase_shouldReturnCorrectPerson_whenPersonAdded() { + PersonDatabase personDatabase = new LockCachingPersonDatabase(); + testDatabase(personDatabase); + } + + @Test + @DisplayName("Тестирование CachingPersonDatabase") + public void synchronizedDatabase_shouldReturnCorrectPerson_whenPersonAdded() { + PersonDatabase personDatabase = new CachingPersonDatabase() { + @SneakyThrows + @Override + public synchronized void add(Person person) { + Thread.sleep(3000); // Imitation of slow database + super.add(person); + } + }; + testDatabase(personDatabase); + } + + @SneakyThrows + private static void testDatabase(PersonDatabase personDatabase) { + var executorService = Executors.newFixedThreadPool(5); + var person = new Person(1, "Ivan", "Moscow", "1234567890"); + executorService.execute(() -> personDatabase.add(person)); + Thread.sleep(100); + Future> futureByPhone = executorService.submit(() -> personDatabase.findByPhone("1234567890")); + Future> futureByName = executorService.submit(() -> personDatabase.findByName("Ivan")); + Future> futureByAddress = executorService.submit(() -> personDatabase.findByAddress("Moscow")); + executorService.shutdown(); + assertAll( + () -> Assertions.assertThat(futureByPhone.get()).contains(person), + () -> Assertions.assertThat(futureByName.get()).contains(person), + () -> Assertions.assertThat(futureByAddress.get()).contains(person) + ); + } + +} diff --git a/src/test/java/edu/hw7/task4/MonteCarloPiComputerTest.java b/src/test/java/edu/hw7/task4/MonteCarloPiComputerTest.java new file mode 100644 index 0000000..015a8a8 --- /dev/null +++ b/src/test/java/edu/hw7/task4/MonteCarloPiComputerTest.java @@ -0,0 +1,24 @@ +package edu.hw7.task4; + +import org.assertj.core.api.Assertions; +import org.assertj.core.data.Offset; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class MonteCarloPiComputerTest { + + @Test + @DisplayName("Тестирование MonteCarloPiComputer#computePI") + public void computePI_shouldReturnCorrectValue() { + Assertions.assertThat(MonteCarloPiComputer.computePI(1000000000)) + .isCloseTo(Math.PI, Offset.offset(0.01)); + } + + @Test + @DisplayName("Тестирование MonteCarloPiComputer#computePIParallel") + public void computePIParallel_shouldReturnCorrectValue() { + Assertions.assertThat(MonteCarloPiComputer.computePIParallel(1000000000, 8)) + .isCloseTo(Math.PI, Offset.offset(0.01)); + } + +}