├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
└── java
└── com
└── sergeyvolkodav
├── barrier
├── Barrier.java
├── Demo.java
└── README.md
├── deferredcallback
├── CallBack.java
├── DeferredCallback.java
├── Demo.java
└── README.md
├── queue
├── BlockingQueue.java
├── Demo.java
└── README.md
├── readwritelock
├── Demo.java
├── README.md
└── ReadWriteLock.java
├── semaphore
├── Demo.java
├── README.md
└── Semaphore.java
├── singleton
├── README.md
└── Singleton.java
├── tokenbucket
├── Demo.java
├── README.md
└── RateLimiting.java
├── uberride
├── Demo.java
├── README.md
└── UberRide.java
└── unisexbathroom
├── Bathroom.java
├── Demo.java
├── Gender.java
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /logs
3 | idea/
4 | *.iml
5 | .idea/
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Sergey Volkodav
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Multithreading Playground
2 |
3 |
4 | ## Problems
5 |
6 | * [Singleton](src/main/java/com/sergeyvolkodav/singleton/README.md)
7 | * [Blocking Queue](src/main/java/com/sergeyvolkodav/queue/README.md)
8 | * [Rate Limiting](src/main/java/com/sergeyvolkodav/tokenbucket/README.md)
9 | * [Deferred Callback](src/main/java/com/sergeyvolkodav/deferredcallback/README.md)
10 | * [Semaphore](src/main/java/com/sergeyvolkodav/semaphore/README.md)
11 | * [Read-write Lock](src/main/java/com/sergeyvolkodav/readwritelock/README.md)
12 | * [Unisex Bathroom Problem](src/main/java/com/sergeyvolkodav/unisexbathroom/README.md)
13 | * [Barrier](src/main/java/com/sergeyvolkodav/barrier/README.md)
14 | * [Uber Ride Problem](src/main/java/com/sergeyvolkodav/uberride/README.md)
15 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.sergeyvolkodav
8 | multithreading-playground
9 | 0.1.0-SNAPSHOT
10 |
11 |
12 | 1.8
13 | 1.8
14 | 5.3.1
15 |
16 |
17 |
18 |
19 | org.junit.jupiter
20 | junit-jupiter-engine
21 | ${junit.version}
22 | test
23 |
24 |
25 |
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-compiler-plugin
31 | 3.6.1
32 |
33 | ${maven.compiler.source}
34 | ${maven.compiler.target}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/barrier/Barrier.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.barrier;
2 |
3 | public class Barrier {
4 |
5 | private int count = 0;
6 | private int released = 0;
7 | private final int totalThreads;
8 |
9 | public Barrier(int totalThreads) {
10 | this.totalThreads = totalThreads;
11 | }
12 |
13 | public synchronized void await() throws InterruptedException {
14 |
15 | while (count == totalThreads) {
16 | wait();
17 | }
18 | count++;
19 | if (count == totalThreads) {
20 | notifyAll();
21 | released = totalThreads;
22 | } else {
23 | while (count < totalThreads) {
24 | wait();
25 | }
26 | }
27 | released--;
28 | if (released == 0) {
29 | count = 0;
30 | notifyAll();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/barrier/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.barrier;
2 |
3 | public class Demo {
4 |
5 |
6 | public static void main(String[] args) throws InterruptedException {
7 | final Barrier barrier = new Barrier(3);
8 |
9 | Thread p1 = new Thread(() -> {
10 | try {
11 | System.out.println("Thread 1");
12 | barrier.await();
13 | System.out.println("Thread 1");
14 | barrier.await();
15 | System.out.println("Thread 1");
16 | barrier.await();
17 | } catch (InterruptedException ie) {
18 | }
19 | });
20 |
21 | Thread p2 = new Thread(() -> {
22 | try {
23 | Thread.sleep(500);
24 | System.out.println("Thread 2");
25 | barrier.await();
26 | Thread.sleep(500);
27 | System.out.println("Thread 2");
28 | barrier.await();
29 | Thread.sleep(500);
30 | System.out.println("Thread 2");
31 | barrier.await();
32 | } catch (InterruptedException ie) {
33 | }
34 | });
35 |
36 | Thread p3 = new Thread(() -> {
37 | try {
38 | Thread.sleep(1500);
39 | System.out.println("Thread 3");
40 | barrier.await();
41 | Thread.sleep(1500);
42 | System.out.println("Thread 3");
43 | barrier.await();
44 | Thread.sleep(1500);
45 | System.out.println("Thread 3");
46 | barrier.await();
47 | } catch (InterruptedException ie) {
48 | }
49 | });
50 |
51 | p1.start();
52 | p2.start();
53 | p3.start();
54 |
55 | p1.join();
56 | p2.join();
57 | p3.join();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/barrier/README.md:
--------------------------------------------------------------------------------
1 | # Barrier
2 |
3 |
4 |
5 | ## Requirement
6 |
7 | Implementing a Barrier, where all or some of the threads need to reach a point at before any one of them is allowed to proceed further.
8 |
9 |
10 | ## Code
11 |
12 | ```java
13 |
14 | public synchronized void await() throws InterruptedException {
15 |
16 | while (count == totalThreads) {
17 | wait();
18 | }
19 |
20 | count++;
21 |
22 | if (count == totalThreads) {
23 | notifyAll();
24 | released = totalThreads;
25 | } else {
26 | while (count < totalThreads) {
27 | wait();
28 | }
29 | }
30 |
31 | released--;
32 | if (released == 0) {
33 | count = 0;
34 | notifyAll();
35 | }
36 | }
37 |
38 | ```
39 |
40 | [return to main page](../../../../../../README.md)
41 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/deferredcallback/CallBack.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.deferredcallback;
2 |
3 | public class CallBack {
4 |
5 | private long executeAt;
6 | private String message;
7 |
8 | public CallBack(long executeAfter, String message) {
9 | this.executeAt = System.currentTimeMillis() + executeAfter * 1000;
10 | this.message = message;
11 | }
12 |
13 | public long getExecuteAt() {
14 | return executeAt;
15 | }
16 |
17 | public void setExecuteAt(long executeAt) {
18 | this.executeAt = executeAt;
19 | }
20 |
21 | public String getMessage() {
22 | return message;
23 | }
24 |
25 | public void setMessage(String message) {
26 | this.message = message;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/deferredcallback/DeferredCallback.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.deferredcallback;
2 |
3 | import java.util.Comparator;
4 | import java.util.PriorityQueue;
5 | import java.util.concurrent.TimeUnit;
6 | import java.util.concurrent.locks.Condition;
7 | import java.util.concurrent.locks.ReentrantLock;
8 |
9 | public class DeferredCallback {
10 |
11 | private PriorityQueue priorityQueue = new PriorityQueue<>(new Comparator() {
12 | @Override
13 | public int compare(CallBack o1, CallBack o2) {
14 | return Math.toIntExact(o1.getExecuteAt() - o2.getExecuteAt());
15 | }
16 | });
17 | private ReentrantLock lock = new ReentrantLock();
18 | private Condition condition = lock.newCondition();
19 |
20 | public void start() throws InterruptedException {
21 | long sleepFor = 0;
22 | long lastSeen = 0;
23 |
24 | while (true) {
25 | lock.lock();
26 |
27 | while (priorityQueue.size() == 0) {
28 | condition.await();
29 | }
30 |
31 | if (lastSeen == priorityQueue.size()) {
32 | condition.await(sleepFor, TimeUnit.MILLISECONDS);
33 | }
34 |
35 | long currentTime = System.currentTimeMillis();
36 | while (priorityQueue.size() != 0 && currentTime >= priorityQueue.peek().getExecuteAt()) {
37 | CallBack callBack = priorityQueue.poll();
38 | System.out.println("Executed at " + System.currentTimeMillis() / 1000
39 | + " required at " + callBack.getExecuteAt() / 1000
40 | + " message " + callBack.getMessage());
41 | }
42 | sleepFor = priorityQueue.size() == 0 ? 0 : priorityQueue.peek().getExecuteAt() - currentTime;
43 | lastSeen = priorityQueue.size();
44 |
45 | lock.unlock();
46 | }
47 | }
48 |
49 | public void registerCallback(CallBack callBack) {
50 | lock.lock();
51 | priorityQueue.add(callBack);
52 | condition.signal();
53 | lock.unlock();
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/deferredcallback/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.deferredcallback;
2 |
3 | import java.util.HashSet;
4 | import java.util.Random;
5 | import java.util.Set;
6 |
7 | public class Demo {
8 |
9 | private static Random random = new Random(System.currentTimeMillis());
10 |
11 | public static void main(String[] args) throws InterruptedException {
12 |
13 | Set allThreads = new HashSet<>();
14 | final DeferredCallback deferredCallbackExecutor = new DeferredCallback();
15 |
16 | Thread service = new Thread(() -> {
17 | try {
18 | deferredCallbackExecutor.start();
19 | } catch (InterruptedException ie) {
20 | }
21 | });
22 |
23 | service.start();
24 |
25 | for (int i = 0; i < 10; i++) {
26 | Thread thread = new Thread(() -> {
27 | CallBack cb = new CallBack(1, "Hello this is " + Thread.currentThread().getName());
28 | deferredCallbackExecutor.registerCallback(cb);
29 | });
30 |
31 | thread.setName("Thread_" + (i + 1));
32 | thread.start();
33 | allThreads.add(thread);
34 | Thread.sleep((random.nextInt(3) + 1) * 1000);
35 | }
36 |
37 | for (Thread t : allThreads) {
38 | t.join();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/deferredcallback/README.md:
--------------------------------------------------------------------------------
1 | # Deferred Callback
2 |
3 | ## Requirement
4 |
5 | Design and implement a thread-safe class that allows registeration of callback methods that are executed after a user specified time interval in seconds has elapsed.
6 |
7 | ## Notes
8 |
9 | * Used java [condition interface](https://stackoverflow.com/questions/10395571/condition-vs-wait-notify-mechanism)
10 | * wait() always must be called inside a loop [read more](https://stackoverflow.com/questions/1038007/why-should-wait-always-be-called-inside-a-loop)
11 |
12 | ## Code
13 |
14 | ```java
15 |
16 | public void start() throws InterruptedException {
17 | long sleepFor = 0;
18 | long lastSeen = 0;
19 |
20 | while (true) {
21 | lock.lock();
22 |
23 | while (priorityQueue.size() == 0) {
24 | condition.await();
25 | }
26 |
27 | if (lastSeen == priorityQueue.size()) {
28 | condition.await(sleepFor, TimeUnit.MILLISECONDS);
29 | }
30 |
31 | long currentTime = System.currentTimeMillis();
32 | while (priorityQueue.size() != 0 && currentTime >= priorityQueue.peek().getExecuteAt()) {
33 | CallBack callBack = priorityQueue.poll();
34 | System.out.println("Executed at " + System.currentTimeMillis() / 1000
35 | + " required at " + callBack.getExecuteAt() / 1000
36 | + " message " + callBack.getMessage());
37 | }
38 | sleepFor = priorityQueue.size() == 0 ? 0 : priorityQueue.peek().getExecuteAt() - currentTime;
39 | lastSeen = priorityQueue.size();
40 |
41 | lock.unlock();
42 | }
43 | }
44 |
45 |
46 | ```
47 |
48 |
49 |
50 | [return to main page](../../../../../../README.md)
51 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/queue/BlockingQueue.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.queue;
2 |
3 | public class BlockingQueue {
4 |
5 | private T[] array;
6 | private int size;
7 | private int capacity;
8 | private int tail;
9 | private int head;
10 | private final Object lock = new Object();
11 |
12 | @SuppressWarnings("unchecked")
13 | public BlockingQueue(int capacity) {
14 | array = (T[]) new Object[capacity];
15 | this.capacity = capacity;
16 | }
17 |
18 | public void enqueue(T item) throws InterruptedException {
19 |
20 | synchronized (lock) {
21 |
22 | while (size == capacity) {
23 | lock.wait();
24 | }
25 |
26 | if (tail == capacity) {
27 | tail = 0;
28 | }
29 |
30 | array[tail] = item;
31 | size++;
32 | tail++;
33 | lock.notify();
34 | }
35 | }
36 |
37 | public T dequeue() throws InterruptedException {
38 |
39 | T item;
40 | synchronized (lock) {
41 |
42 | while (size == 0) {
43 | lock.wait();
44 | }
45 |
46 | if (head == capacity) {
47 | head = 0;
48 | }
49 |
50 | item = array[head];
51 | array[head] = null;
52 | head++;
53 | size--;
54 |
55 | lock.notify();
56 | }
57 | return item;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/queue/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.queue;
2 |
3 | public class Demo {
4 |
5 | public static void main(String[] args) throws InterruptedException {
6 |
7 | BlockingQueue blockingQueue = new BlockingQueue<>(5);
8 |
9 | Thread thread1 = new Thread(() -> {
10 |
11 | for (int i = 0; i <= 50; i++) {
12 | try {
13 | blockingQueue.enqueue(i);
14 | System.out.println("Thread 1 enqueued " + i);
15 |
16 | } catch (InterruptedException e) {
17 | }
18 | }
19 | });
20 |
21 |
22 | Thread thread2 = new Thread(() -> {
23 | for (int i = 0; i <= 25; i++) {
24 | try {
25 | System.out.println("Thread 2 dequeued: " + blockingQueue.dequeue());
26 | } catch (InterruptedException e) {
27 |
28 | }
29 | }
30 | });
31 | Thread thread3 = new Thread(() -> {
32 | for (int i = 0; i <= 25; i++) {
33 | try {
34 | System.out.println("Thread 3 dequeued: " + blockingQueue.dequeue());
35 |
36 | } catch (InterruptedException e) {
37 | }
38 | }
39 | });
40 |
41 |
42 |
43 | thread1.start();
44 | Thread.sleep(1000);
45 | thread2.start();
46 | thread3.start();
47 |
48 | thread1.join();
49 | thread2.join();
50 | thread3.join();
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/queue/README.md:
--------------------------------------------------------------------------------
1 | # Singleton
2 |
3 | ## Requirement
4 |
5 | * Tread safe
6 | * Fixed size
7 | * Generics
8 |
9 |
10 | ## Notes
11 |
12 | * wait() always must be called inside a loop [read more](https://stackoverflow.com/questions/1038007/why-should-wait-always-be-called-inside-a-loop)
13 |
14 |
15 | ## Code
16 |
17 | ### Enqueue
18 |
19 | ```java
20 | public void enqueue(T item) throws InterruptedException {
21 |
22 | synchronized (lock) {
23 |
24 | while (size == capacity) {
25 | lock.wait();
26 | }
27 |
28 | if (tail == capacity) {
29 | tail = 0;
30 | }
31 |
32 | array[tail] = item;
33 | size++;
34 | tail++;
35 | lock.notify();
36 | }
37 | }
38 |
39 | ```
40 |
41 | ### Enqueue
42 |
43 | ```java
44 | public T dequeue() throws InterruptedException {
45 |
46 | T item;
47 | synchronized (lock) {
48 |
49 | while (size == 0) {
50 | lock.wait();
51 | }
52 |
53 | if (head == capacity) {
54 | head = 0;
55 | }
56 |
57 | item = array[head];
58 | array[head] = null;
59 | head++;
60 | size--;
61 |
62 | lock.notify();
63 | }
64 | return item;
65 | }
66 | ```
67 |
68 | [return to main page](../../../../../../README.md)
69 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/readwritelock/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.readwritelock;
2 |
3 | public class Demo {
4 |
5 | public static void main(String[] args) throws InterruptedException {
6 |
7 | final ReadWriteLock readWriteLock = new ReadWriteLock();
8 |
9 | Thread t1 = new Thread(() -> {
10 | try {
11 |
12 | System.out.println("Attempting to acquire write lock in thread 1: " + System.currentTimeMillis());
13 | readWriteLock.acquireWriteLock();
14 | System.out.println("write lock acquired thread 1: " + +System.currentTimeMillis());
15 |
16 | // Simulates write lock being held indefinitely
17 | for (; ; ) {
18 | Thread.sleep(500);
19 | }
20 |
21 | } catch (InterruptedException ie) {
22 |
23 | }
24 | });
25 |
26 | Thread t2 = new Thread(() -> {
27 | try {
28 |
29 | System.out.println("Attempting to acquire write lock in thread 2: " + System.currentTimeMillis());
30 | readWriteLock.acquireWriteLock();
31 | System.out.println("write lock acquired thread 2: " + System.currentTimeMillis());
32 |
33 | } catch (InterruptedException ie) {
34 |
35 | }
36 | });
37 |
38 | Thread tReader1 = new Thread(() -> {
39 | try {
40 |
41 | readWriteLock.acquireReadLock();
42 | System.out.println("Read lock acquired: " + System.currentTimeMillis());
43 |
44 | } catch (InterruptedException ie) {
45 | }
46 | });
47 |
48 | Thread tReader2 = new Thread(() -> {
49 |
50 | System.out.println("Read lock about to release: " + System.currentTimeMillis());
51 | try {
52 | readWriteLock.releaseReadLock();
53 | System.out.println("Read lock released: " + System.currentTimeMillis());
54 | } catch (InterruptedException e) {
55 | }
56 | });
57 |
58 | tReader1.start();
59 | t1.start();
60 | Thread.sleep(3000);
61 | tReader2.start();
62 | Thread.sleep(1000);
63 | t2.start();
64 | tReader1.join();
65 | tReader2.join();
66 | t2.join();
67 | }
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/readwritelock/README.md:
--------------------------------------------------------------------------------
1 | # Read-Write Lock
2 |
3 | ## Requirement
4 |
5 | Design a lock which lets multiple readers read at the same time, but only one writer write at a time.
6 |
7 | ## Notes
8 |
9 | * it is possible for a writer to [starve](https://docs.oracle.com/javase/tutorial/essential/concurrency/starvelive.html) and never get a chance to acquire the write lock since there could always be at least one reader which has the read lock acquired.
10 | * wait() always must be called inside a loop [read more](https://stackoverflow.com/questions/1038007/why-should-wait-always-be-called-inside-a-loop)
11 |
12 |
13 | ## Code
14 |
15 | ```java
16 |
17 | public synchronized void acquireReadLock() throws InterruptedException {
18 | while (isWriteLocked) {
19 | wait();
20 | }
21 | readers++;
22 | }
23 |
24 | public synchronized void releaseReadLock() throws InterruptedException {
25 | while (readers == 0) {
26 | wait();
27 | }
28 | readers--;
29 | notify();
30 | }
31 |
32 | public synchronized void acquireWriteLock() throws InterruptedException {
33 | while (isWriteLocked && readers != 0) {
34 | wait();
35 | }
36 | isWriteLocked = true;
37 | }
38 |
39 | public synchronized void releaseWriteLock() {
40 | isWriteLocked = false;
41 | notify();
42 | }
43 |
44 | ```
45 |
46 | [return to main page](../../../../../../README.md)
47 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/readwritelock/ReadWriteLock.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.readwritelock;
2 |
3 | public class ReadWriteLock {
4 |
5 | private int readers = 0;
6 | private boolean isWriteLocked = false;
7 |
8 |
9 | public synchronized void acquireReadLock() throws InterruptedException {
10 | while (isWriteLocked) {
11 | wait();
12 | }
13 | readers++;
14 | }
15 |
16 | public synchronized void releaseReadLock() throws InterruptedException {
17 | while (readers == 0) {
18 | wait();
19 | }
20 | readers--;
21 | notify();
22 | }
23 |
24 | public synchronized void acquireWriteLock() throws InterruptedException {
25 | while (isWriteLocked && readers != 0) {
26 | wait();
27 | }
28 | isWriteLocked = true;
29 | }
30 |
31 | public synchronized void releaseWriteLock() {
32 | isWriteLocked = false;
33 | notify();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/semaphore/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.semaphore;
2 |
3 | public class Demo {
4 |
5 |
6 | public static void main(String[] args) throws InterruptedException {
7 |
8 | Semaphore semaphore = new Semaphore(1);
9 |
10 | Thread ping = new Thread(() -> {
11 | try {
12 | for (int i = 0; i < 10; i++) {
13 | semaphore.acquire();
14 | System.out.println("ping");
15 | }
16 | } catch (InterruptedException e) {
17 | }
18 | });
19 |
20 | Thread pong = new Thread(() -> {
21 | try {
22 | for (int i = 0; i < 10; i++) {
23 | semaphore.release();
24 | System.out.println("pong");
25 | }
26 | } catch (InterruptedException e) {
27 | }
28 | });
29 |
30 | ping.start();
31 | pong.start();
32 | ping.join();
33 | pong.join();
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/semaphore/README.md:
--------------------------------------------------------------------------------
1 | # Semaphore
2 |
3 | ## Requirement
4 |
5 | Implement a semaphore which takes in its constructor the maximum number of permits allowed and is also initialized with the same number of permits.
6 |
7 | ## Notes
8 |
9 | * [Semaphore vs Mutex](https://stackoverflow.com/questions/62814/difference-between-binary-semaphore-and-mutex)
10 | * wait() always must be called inside a loop [read more](https://stackoverflow.com/questions/1038007/why-should-wait-always-be-called-inside-a-loop)
11 |
12 |
13 | ## Code
14 |
15 | ```java
16 |
17 | public synchronized void acquire() throws InterruptedException {
18 |
19 | while (permits == count) {
20 | wait();
21 | }
22 | permits++;
23 | notify();
24 | }
25 |
26 | public synchronized void release() throws InterruptedException {
27 | while (permits == 0) {
28 | wait();
29 | }
30 | permits--;
31 | notify();
32 | }
33 | ```
34 |
35 | [return to main page](../../../../../../README.md)
36 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/semaphore/Semaphore.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.semaphore;
2 |
3 | public class Semaphore {
4 |
5 | private int permits;
6 | private int maxCount;
7 |
8 |
9 | public Semaphore(int maxCount) {
10 | this.maxCount = maxCount;
11 | }
12 |
13 | public synchronized void acquire() throws InterruptedException {
14 | while (permits == maxCount) {
15 | wait();
16 | }
17 | permits++;
18 | notify();
19 | }
20 |
21 | public synchronized void release() throws InterruptedException {
22 | while (permits == 0) {
23 | wait();
24 | }
25 | permits--;
26 | notify();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/singleton/README.md:
--------------------------------------------------------------------------------
1 | # Singleton
2 |
3 | ## Requirement
4 |
5 | * Create an object on request
6 | * Thread safe
7 | * Small overhead on synchronization
8 |
9 |
10 | ## Notes
11 |
12 | * [Block synchronization](http://www.java67.com/2013/01/difference-between-synchronized-block-vs-method-java-example.html)
13 | * Volatile keyword for [happens-before](https://en.wikipedia.org/wiki/Java_memory_model) semantics
14 | * Lazy initialization
15 | * [Double-Checked Locking](https://en.wikipedia.org/wiki/Double-checked_locking)
16 |
17 | ## Code
18 |
19 | ```java
20 | public static Singleton getInstance() {
21 | if (singleton == null) {
22 | synchronized (Singleton.class) {
23 | if (singleton == null) {
24 | singleton = new Singleton();
25 | System.out.println("Singleton object created");
26 | }
27 | }
28 | }
29 | return singleton;
30 | }
31 | ```
32 |
33 | [return to main page](../../../../../../README.md)
34 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/singleton/Singleton.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.singleton;
2 |
3 | public class Singleton {
4 |
5 | private static volatile Singleton singleton;
6 |
7 | private Singleton() {
8 | }
9 |
10 | public static Singleton getInstance() {
11 | if (singleton == null) {
12 |
13 | synchronized (Singleton.class) {
14 | if (singleton == null) {
15 | singleton = new Singleton();
16 | System.out.println("Singleton object created");
17 | }
18 | }
19 | }
20 | return singleton;
21 | }
22 |
23 | public void doStuff() {
24 | System.out.println("I'm doing something very important");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/tokenbucket/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.tokenbucket;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | public class Demo {
7 |
8 | public static void main(String[] args) throws InterruptedException {
9 |
10 | Set allThreads = new HashSet<>();
11 | final RateLimiting tokenBucketFilter = new RateLimiting(10);
12 |
13 | for (int i = 0; i < 50; i++) {
14 |
15 | Thread thread = new Thread(
16 | () -> {
17 | try {
18 | tokenBucketFilter.getToken();
19 | } catch (InterruptedException ie) {
20 | }
21 | });
22 | thread.setName("Thread_" + (i + 1));
23 | allThreads.add(thread);
24 | }
25 |
26 | for (Thread t : allThreads) {
27 | t.start();
28 | }
29 |
30 | for (Thread t : allThreads) {
31 | t.join();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/tokenbucket/README.md:
--------------------------------------------------------------------------------
1 | # Rate Limit
2 |
3 | ## Requirement
4 |
5 |
6 | Imagine you have a bucket that gets filled with tokens at the rate of 1 token per second.
7 | The bucket can hold a maximum of N tokens. Implement a thread-safe class that lets threads get a token when one is available.
8 | If no token is available, then the token-requesting threads should block.
9 |
10 | The class should expose an API called getToken that various threads can call to get a token
11 |
12 |
13 | ## Notes
14 |
15 | * 1 request per second is a slowest
16 | * Synchronized keyword on a method
17 | * wait() always must be called inside a loop [read more](https://stackoverflow.com/questions/1038007/why-should-wait-always-be-called-inside-a-loop)
18 |
19 |
20 | ## Code
21 |
22 | ```java
23 |
24 | public synchronized void getToken() throws InterruptedException {
25 |
26 | possibleTokens += (System.currentTimeMillis() - lastRequestTime) / delay;
27 |
28 | if (possibleTokens > maxTokens) {
29 | possibleTokens = maxTokens;
30 | }
31 |
32 | if (possibleTokens == 0) {
33 | Thread.sleep(delay);
34 | } else {
35 | possibleTokens--;
36 | }
37 | lastRequestTime = System.currentTimeMillis();
38 |
39 | System.out.println("Granting " + Thread.currentThread().getName() + " token at " + (Instant.now()));
40 |
41 | }
42 |
43 | ```
44 |
45 |
46 |
47 |
48 | [return to main page](../../../../../../README.md)
49 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/tokenbucket/RateLimiting.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.tokenbucket;
2 |
3 | import java.time.Instant;
4 |
5 | public class RateLimiting {
6 |
7 | private int maxTokens;
8 | private long lastRequestTime = System.currentTimeMillis();
9 | private long possibleTokens = 0;
10 | private int delay;
11 |
12 | public RateLimiting(int maxTokens) {
13 | // 1 token per second is a minimum
14 | this.maxTokens = Math.max(maxTokens, 1);
15 | this.delay = 1000 / maxTokens;
16 | }
17 |
18 | public synchronized void getToken() throws InterruptedException {
19 |
20 | possibleTokens += (System.currentTimeMillis() - lastRequestTime) / delay;
21 |
22 | if (possibleTokens > maxTokens) {
23 | possibleTokens = maxTokens;
24 | }
25 |
26 | if (possibleTokens == 0) {
27 | Thread.sleep(delay);
28 | } else {
29 | possibleTokens--;
30 | }
31 | lastRequestTime = System.currentTimeMillis();
32 |
33 | System.out.println("Granting " + Thread.currentThread().getName() + " token at " + (Instant.now()));
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/uberride/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.uberride;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 | import java.util.concurrent.BrokenBarrierException;
6 |
7 | public class Demo {
8 |
9 | public static void main(String[] args) throws InterruptedException {
10 |
11 | final UberRide uberSeatingProblem = new UberRide();
12 | Set allThreads = new HashSet<>();
13 |
14 | for (int i = 0; i < 10; i++) {
15 | Thread thread = new Thread(() -> {
16 | try {
17 | uberSeatingProblem.seatDemocrat();
18 | } catch (InterruptedException | BrokenBarrierException ie) {
19 | }
20 | });
21 | thread.setName("Democrat_" + (i + 1));
22 | allThreads.add(thread);
23 |
24 | Thread.sleep(50);
25 | }
26 |
27 | for (int i = 0; i < 14; i++) {
28 | Thread thread = new Thread(() -> {
29 | try {
30 | uberSeatingProblem.setRepublican();
31 | } catch (InterruptedException | BrokenBarrierException ie) {
32 | }
33 | });
34 | thread.setName("Republican_" + (i + 1));
35 | allThreads.add(thread);
36 | Thread.sleep(20);
37 | }
38 |
39 | for (Thread t : allThreads) {
40 | t.start();
41 | }
42 |
43 | for (Thread t : allThreads) {
44 | t.join();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/uberride/README.md:
--------------------------------------------------------------------------------
1 | # Uber Ride Problem
2 |
3 | ## Requirement
4 |
5 | Imagine at the end of a political conference, republicans and democrats are trying to
6 | leave the venue and ordering Uber rides at the same time. However, to make sure no fight
7 | breaks out in an Uber ride, the software developers at Uber come up with an algorithm
8 | whereby either an Uber ride can have all democrats or republicans or two Democrats and
9 | two Republicans. All other combinations can result in a fist-fight.
10 |
11 |
12 | ## Code
13 |
14 | ```java
15 |
16 | public void seatDemocrat() throws InterruptedException, BrokenBarrierException {
17 | boolean riderLeader = false;
18 | lock.lock();
19 |
20 | democrats++;
21 | if (democrats == 4) {
22 | demsWaiting.release(3);
23 | democrats -= 4;
24 | riderLeader = true;
25 | } else if (democrats == 2 && republicans >= 2) {
26 | demsWaiting.release(1);
27 | repubsWaiting.release(2);
28 | riderLeader = true;
29 | democrats -= 2;
30 | republicans -= 2;
31 | } else {
32 | lock.unlock();
33 | demsWaiting.acquire();
34 | }
35 | seated();
36 | barrier.await();
37 |
38 | if (riderLeader == true) {
39 | drive();
40 | lock.unlock();
41 | }
42 | }
43 |
44 | ```
45 |
46 |
47 | [return to main page](../../../../../../README.md)
48 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/uberride/UberRide.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.uberride;
2 |
3 | import java.util.concurrent.BrokenBarrierException;
4 | import java.util.concurrent.CyclicBarrier;
5 | import java.util.concurrent.Semaphore;
6 | import java.util.concurrent.locks.ReentrantLock;
7 |
8 | public class UberRide {
9 |
10 | private int republicans = 0;
11 | private int democrats = 0;
12 |
13 | private Semaphore demsWaiting = new Semaphore(0);
14 | private Semaphore repubsWaiting = new Semaphore(0);
15 |
16 | CyclicBarrier barrier = new CyclicBarrier(4);
17 | ReentrantLock lock = new ReentrantLock();
18 |
19 | public void seatDemocrat() throws InterruptedException, BrokenBarrierException {
20 | boolean riderLeader = false;
21 | lock.lock();
22 |
23 | democrats++;
24 | if (democrats == 4) {
25 | demsWaiting.release(3);
26 | democrats -= 4;
27 | riderLeader = true;
28 | } else if (democrats == 2 && republicans >= 2) {
29 | demsWaiting.release(1);
30 | repubsWaiting.release(2);
31 | riderLeader = true;
32 | democrats -= 2;
33 | republicans -= 2;
34 | } else {
35 | lock.unlock();
36 | demsWaiting.acquire();
37 | }
38 | seated();
39 | barrier.await();
40 |
41 | if (riderLeader == true) {
42 | drive();
43 | lock.unlock();
44 | }
45 | }
46 |
47 | public void setRepublican() throws InterruptedException, BrokenBarrierException {
48 | boolean riderLeader = false;
49 | lock.lock();
50 |
51 | republicans++;
52 | if (republicans == 4) {
53 | repubsWaiting.release(3);
54 | republicans -= 4;
55 | riderLeader = true;
56 | } else if (republicans == 2 && democrats >= 2) {
57 | repubsWaiting.release(1);
58 | demsWaiting.release(2);
59 | riderLeader = true;
60 | democrats -= 2;
61 | republicans -= 2;
62 | } else {
63 | lock.unlock();
64 | repubsWaiting.acquire();
65 | }
66 | seated();
67 | barrier.await();
68 | if (riderLeader == true) {
69 | drive();
70 | lock.unlock();
71 | }
72 |
73 | }
74 |
75 | void seated() {
76 | System.out.println(Thread.currentThread().getName() + " seated");
77 | }
78 |
79 | void drive() {
80 | System.out.println("Uber Ride on Its way... with ride leader " + Thread.currentThread().getName());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/unisexbathroom/Bathroom.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.unisexbathroom;
2 |
3 | import java.util.concurrent.Semaphore;
4 |
5 | public class Bathroom {
6 |
7 | private Gender isGender = Gender.NONE;
8 | private int emps = 0;
9 | Semaphore maxEmps = new Semaphore(3);
10 |
11 | public void maleUseBathroom(String name) throws InterruptedException {
12 |
13 | synchronized (this) {
14 | while (isGender == Gender.FEMALE) {
15 | wait();
16 | }
17 | maxEmps.acquire();
18 | isGender = Gender.MEN;
19 | emps++;
20 | }
21 |
22 | useBathroom(Gender.MEN);
23 | maxEmps.release();
24 |
25 | synchronized (this) {
26 | emps--;
27 | if (emps == 0) {
28 | isGender = Gender.NONE;
29 | }
30 | notifyAll();
31 | }
32 | }
33 |
34 | public void femaleUseBathroom(String name) throws InterruptedException {
35 |
36 | synchronized (this) {
37 |
38 | while (isGender == Gender.MEN) {
39 | wait();
40 | }
41 | maxEmps.acquire();
42 | isGender = Gender.FEMALE;
43 | emps++;
44 | }
45 |
46 | useBathroom(Gender.FEMALE);
47 | maxEmps.release();
48 |
49 | synchronized (this) {
50 | emps--;
51 | if (emps == 0) {
52 | isGender = Gender.NONE;
53 | }
54 | notifyAll();
55 | }
56 | }
57 |
58 | private void useBathroom(Gender gender) throws InterruptedException {
59 | System.out.println(gender.name() + " using bathroom. Current employees in bathroom = " + emps);
60 | Thread.sleep(3500);
61 | System.out.println("Done with bathroom");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/unisexbathroom/Demo.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.unisexbathroom;
2 |
3 | public class Demo {
4 |
5 | public static void main(String[] args) throws InterruptedException {
6 | final Bathroom unisexBathroom = new Bathroom();
7 |
8 | Thread female1 = new Thread(() -> {
9 | try {
10 | unisexBathroom.femaleUseBathroom("Mary");
11 | } catch (InterruptedException ie) {
12 | }
13 | });
14 |
15 | Thread male1 = new Thread(() -> {
16 | try {
17 | unisexBathroom.maleUseBathroom("Alessandro");
18 | } catch (InterruptedException ie) {
19 |
20 | }
21 | });
22 |
23 | Thread male2 = new Thread(() -> {
24 | try {
25 | unisexBathroom.maleUseBathroom("Claudio");
26 | } catch (InterruptedException ie) {
27 | }
28 | });
29 |
30 | Thread male3 = new Thread(() -> {
31 | try {
32 | unisexBathroom.maleUseBathroom("John");
33 | } catch (InterruptedException ie) {
34 | }
35 | });
36 |
37 | Thread male4 = new Thread(() -> {
38 | try {
39 | unisexBathroom.maleUseBathroom("Sergii");
40 | } catch (InterruptedException ie) {
41 | }
42 | });
43 |
44 | female1.start();
45 | male1.start();
46 | male2.start();
47 | male3.start();
48 | male4.start();
49 |
50 | female1.join();
51 | male1.join();
52 | male2.join();
53 | male3.join();
54 | male4.join();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/unisexbathroom/Gender.java:
--------------------------------------------------------------------------------
1 | package com.sergeyvolkodav.unisexbathroom;
2 |
3 | public enum Gender {
4 | MEN, FEMALE, NONE
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/sergeyvolkodav/unisexbathroom/README.md:
--------------------------------------------------------------------------------
1 | # Unisex Bathroom Problem
2 |
3 | ## Requirement
4 |
5 | A bathroom is being designed for the use of both males and females in an office but requires the following constraints to be maintained:
6 |
7 | * There cannot be men and women in the bathroom at the same time.
8 | * There should never be more than three employees in the bathroom simultaneously.
9 |
10 | ## Notes
11 | * No deadlock
12 | * Potential [starvation](https://docs.oracle.com/javase/tutorial/essential/concurrency/starvelive.html)
13 |
14 | ## Code
15 |
16 | ```java
17 |
18 | public void maleUseBathroom(String name) throws InterruptedException {
19 |
20 | synchronized (this) {
21 | while (isGender == Gender.FEMALE) {
22 | wait();
23 | }
24 | maxEmps.acquire();
25 | isGender = Gender.MEN;
26 | emps++;
27 | }
28 |
29 | useBathroom(Gender.MEN);
30 | maxEmps.release();
31 |
32 | synchronized (this) {
33 | emps--;
34 | if (emps == 0) {
35 | isGender = Gender.NONE;
36 | }
37 | notifyAll();
38 | }
39 | }
40 |
41 | public void femaleUseBathroom(String name) throws InterruptedException {
42 |
43 | synchronized (this) {
44 |
45 | while (isGender == Gender.MEN) {
46 | wait();
47 | }
48 | maxEmps.acquire();
49 | isGender = Gender.FEMALE;
50 | emps++;
51 | }
52 |
53 | useBathroom(Gender.FEMALE);
54 | maxEmps.release();
55 |
56 | synchronized (this) {
57 | emps--;
58 | if (emps == 0) {
59 | isGender = Gender.NONE;
60 | }
61 | notifyAll();
62 | }
63 | }
64 |
65 | ```
66 |
67 |
68 | [return to main page](../../../../../../README.md)
69 |
--------------------------------------------------------------------------------