├── .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 | --------------------------------------------------------------------------------