├── .gitignore ├── Cancellation ├── Cancellation │ ├── Cancelling tasks │ │ ├── src │ │ │ └── Task6.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests6.java │ ├── Handling interruption │ │ ├── src │ │ │ └── Task5.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests5.java │ ├── Stopping threads │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── CompletableFuture ├── Combining futures │ ├── Wait for all │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Wait for first │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Creating futures │ ├── Complete on callback │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Create in specified thread pool │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Creating in default pool │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Transforming futures │ ├── Chaining │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Timeouts │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests8.java │ ├── Transforming │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Data structures ├── Atomic classes │ ├── Arbitrary references │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Boolean flag │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests10.java │ ├── Changing the value │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Incrementing counter │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Maps │ ├── Atomic transformation │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Concurrent maps overview │ │ ├── src │ │ │ └── Main.java │ │ ├── task-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Queues │ ├── Basic consumer │ │ ├── src │ │ │ ├── Job.java │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ ├── TestJob.java │ │ │ └── Tests.java │ ├── Ordered concurrency │ │ ├── src │ │ │ ├── CustomerJob.java │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests11.java │ ├── Single consumer │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests9.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Introduction ├── Computer architecture │ ├── CPU basics │ │ ├── src │ │ │ └── Main.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── Terms and definitions │ │ ├── src │ │ │ └── Main.java │ │ ├── task-info.yaml │ │ ├── task.md │ │ └── tweet.png │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Welcome │ ├── Hello World │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Java Memory Model ├── Java Memory Model │ ├── Guarantees │ │ ├── src │ │ │ └── Main.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Monitoring ├── Thread pools │ ├── Micrometer │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Queue size │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── Task runtime │ │ ├── src │ │ │ ├── CallableWithTaskRuntime.java │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Task waiting time │ │ ├── src │ │ │ ├── CallableWithWaitTime.java │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── README.md ├── Synchronization ├── CountDownLatch │ ├── Multiple threads waiting │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── Wait for multiple threads to finish │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Exclusive locks │ ├── ReentrantLock │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── Synchronized │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Semaphore │ ├── No concurrent jobs │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ └── task.md │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Thread pools ├── Creating │ ├── Fixed thread pool with custom thread naming │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Fixed thread pool with limited queue │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests4.java │ ├── Fixed thread pool │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests3.java │ ├── Single thread exeutor with limited queue │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Single thread pool │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Scheduling │ ├── Schedule task in the future │ │ ├── src │ │ │ └── Task7.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests7.java │ ├── Scheduling with fixed delay │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Scheduling with fixed interval │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Shutdown │ └── lesson-info.yaml ├── Tuning │ ├── Handling rejection │ │ ├── src │ │ │ ├── SystemOverflowException.java │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── Threads ├── Creating │ ├── Creating plain threads │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests2.java │ ├── Do not extend Thread │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Start plain threads │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── Managing │ ├── Naming threads │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── Waiting for thread to complete │ │ ├── src │ │ │ └── Task.java │ │ ├── task-info.yaml │ │ ├── task-remote-info.yaml │ │ ├── task.md │ │ └── test │ │ │ └── Tests.java │ ├── lesson-info.yaml │ └── lesson-remote-info.yaml ├── section-info.yaml └── section-remote-info.yaml ├── build.gradle ├── course-info.yaml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── util └── src └── test └── java ├── ConcurrentAssertions.java ├── ErrorCollectingRunnable.java ├── Events.java ├── FutureFailingWhenBlocked.java ├── Sleeper.java └── UncheckedRunnable.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | .coursecreator 4 | build 5 | classes -------------------------------------------------------------------------------- /Cancellation/Cancellation/Cancelling tasks/src/Task6.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.time.Instant; 3 | import java.util.concurrent.Callable; 4 | 5 | public class Task6 { 6 | 7 | Callable task(Runnable onComplete, Runnable onInterrupt) { 8 | return () -> { 9 | Instant start = Instant.now(); 10 | while(Duration.between(start, Instant.now()).toMillis() < 1000) { 11 | //doing work... 12 | //TODO Here 13 | } 14 | onComplete.run(); 15 | return null; 16 | }; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/Cancelling tasks/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task6.java 4 | visible: true 5 | - name: test/Tests6.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Cancelling tasks/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 878507330 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Cancelling tasks/task.md: -------------------------------------------------------------------------------- 1 | Use `Thread.currentThread().isInterrupted()` flag to check if the task was interrupted by someone else. 2 | If that's the case, stop the task gracefully by calling `onInterrupt` callback. 3 | The task should stop immediately. -------------------------------------------------------------------------------- /Cancellation/Cancellation/Cancelling tasks/test/Tests6.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Callable; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ExecutionException; 4 | 5 | import com.google.common.base.Throwables; 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | 9 | import static org.mockito.Mockito.never; 10 | import static org.mockito.Mockito.verify; 11 | 12 | public class Tests6 { 13 | 14 | @Test 15 | public void testSolution() throws InterruptedException { 16 | CountDownLatch wasInterrupted = new CountDownLatch(1); 17 | Callable task = new Task6().task(() -> {}, wasInterrupted::countDown); 18 | 19 | Thread thread = new Thread(() -> { 20 | try { 21 | task.call(); 22 | } catch (Exception e) { 23 | Throwables.throwIfUnchecked(e); 24 | throw new RuntimeException(e); 25 | } 26 | }); 27 | thread.start(); 28 | Thread.sleep(100); 29 | thread.interrupt(); 30 | ConcurrentAssertions.waitFor(wasInterrupted, "task to be interrupted"); 31 | } 32 | 33 | @Test 34 | public void interruptPrematurelyStopsTask() throws InterruptedException, ExecutionException { 35 | CountDownLatch wasInterrupted = new CountDownLatch(1); 36 | Runnable onComplete = Mockito.mock(Runnable.class); 37 | Callable task = new Task6().task(onComplete, wasInterrupted::countDown); 38 | Thread thread = new Thread(() -> { 39 | try { 40 | task.call(); 41 | } catch (Exception e) { 42 | Throwables.throwIfUnchecked(e); 43 | throw new RuntimeException(e); 44 | } 45 | }); 46 | thread.start(); 47 | Thread.sleep(100); 48 | thread.interrupt(); 49 | ConcurrentAssertions.waitFor(wasInterrupted, "task to be interrupted"); 50 | thread.join(); 51 | verify(onComplete, never()).run(); 52 | } 53 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/Handling interruption/src/Task5.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Callable; 2 | 3 | public class Task5 { 4 | Thread create(Callable task, Runnable onCancel) { 5 | throw new UnsupportedOperationException("TODO"); 6 | } 7 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/Handling interruption/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task5.java 4 | visible: true 5 | - name: test/Tests5.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Handling interruption/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 556804517 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Handling interruption/task.md: -------------------------------------------------------------------------------- 1 | Return an un-started `Thread` that runs `task`. 2 | However, when `task.call()` throws `InterruptedException`, catch it and call `onCancel` callback. 3 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Handling interruption/test/Tests5.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.Semaphore; 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class Tests5 { 10 | @Test 11 | public void testSolution() throws InterruptedException { 12 | Semaphore semaphore = new Semaphore(0); 13 | AtomicBoolean cancelled = new AtomicBoolean(); 14 | CountDownLatch started = new CountDownLatch(1); 15 | Thread thread = new Task5().create( 16 | () -> { 17 | started.countDown(); 18 | semaphore.acquire(); 19 | return null; 20 | }, 21 | () -> cancelled.set(true)); 22 | thread.start(); 23 | ConcurrentAssertions.waitFor(started, "task start"); 24 | thread.interrupt(); 25 | assertThat(cancelled).isTrue(); 26 | } 27 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/Stopping threads/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | 3 | void interrupt(Thread thread) { 4 | throw new UnsupportedOperationException("TODO"); 5 | } 6 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/Stopping threads/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | feedback_link: https://nurkiewicz.com/feedback 3 | files: 4 | - name: src/Task.java 5 | visible: true 6 | - name: test/Tests.java 7 | visible: true 8 | 9 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Stopping threads/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 343961457 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/Stopping threads/task.md: -------------------------------------------------------------------------------- 1 | Call `interrupt()` on a `Thread` to stop it gracefully. 2 | 3 | There used be a method `Thread.stop()` but it's no longer recommended. -------------------------------------------------------------------------------- /Cancellation/Cancellation/Stopping threads/test/Tests.java: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test; 2 | import org.mockito.Mockito; 3 | 4 | import static org.mockito.Mockito.verify; 5 | 6 | public class Tests { 7 | 8 | @Test 9 | public void testSolution() { 10 | Thread mock = Mockito.mock(Thread.class); 11 | new Task().interrupt(mock); 12 | verify(mock).interrupt(); 13 | } 14 | } -------------------------------------------------------------------------------- /Cancellation/Cancellation/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Handling interruption 3 | - Stopping threads 4 | - Cancelling tasks 5 | -------------------------------------------------------------------------------- /Cancellation/Cancellation/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1933058426 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Cancellation/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Cancellation 3 | -------------------------------------------------------------------------------- /Cancellation/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 398290644 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for all/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.time.LocalDate; 2 | import java.time.LocalDateTime; 3 | import java.time.LocalTime; 4 | import java.util.concurrent.CompletableFuture; 5 | 6 | public class Task { 7 | CompletableFuture combine(CompletableFuture date, CompletableFuture time) { 8 | throw new UnsupportedOperationException("TODO"); 9 | // return CompletableFuture.completedFuture(date.join().atTime(time.join())); 10 | } 11 | } -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for all/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for all/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1781529858 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for all/task.md: -------------------------------------------------------------------------------- 1 | "Wait" for both `date` and `time` futures, without blocking. 2 | Create a `CompletableFuture` that combines the results of both. 3 | 4 | It's tempting to do this: 5 | 6 | ```java 7 | return CompletableFuture.completedFuture 8 | (date.join().atTime(time.join())); 9 | ``` 10 | But it's completely broken 11 | 12 |
13 | 14 | * Use `CompletableFuture.themCombine` OR: 15 | 16 | * Use `CompletableFuture.allOf()`. 17 | * You will also need `CompletableFuture.join()` but here you are guaranteed it won't block. 18 | 19 |
-------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for all/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.time.LocalDate; 2 | import java.time.LocalDateTime; 3 | import java.time.LocalTime; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.ExecutionException; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.TimeoutException; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.Timeout; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | public class Tests { 15 | @Test 16 | public void testSolution() throws ExecutionException, InterruptedException, TimeoutException { 17 | CompletableFuture future = new Task().combine( 18 | CompletableFuture.completedFuture(LocalDate.of(2022, 1, 1)), 19 | CompletableFuture.completedFuture(LocalTime.of(15, 10))); 20 | assertThat(future.get(1, TimeUnit.SECONDS)).isEqualTo(LocalDateTime.of(2022, 1, 1, 15, 10)); 21 | } 22 | 23 | @Test 24 | @Timeout(1) 25 | public void testShouldNotBlock() { 26 | CompletableFuture future = new Task().combine( 27 | new CompletableFuture<>(), 28 | new CompletableFuture<>()); 29 | //does not block 30 | } 31 | } -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for first/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | 3 | public class Task { 4 | 5 | CompletableFuture firstOf(CompletableFuture f1, CompletableFuture f2) { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for first/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for first/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1684953900 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for first/task.md: -------------------------------------------------------------------------------- 1 | You have two futures from with the same response from two different servers. 2 | One from a server nearby. 3 | Another from a server on another continent. 4 | You don't know which one is which. 5 | You only care about the very first response. 6 | 7 | Create a `CompletableFuture` that completes when the first future completes. 8 | No matter which one. 9 | 10 |
11 | 12 | Use `CompletableFuture.applyToEither()` or `CompletableFuture.anyOf()` 13 | 14 |
-------------------------------------------------------------------------------- /CompletableFuture/Combining futures/Wait for first/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.ExecutionException; 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | public class Tests { 11 | 12 | @Test 13 | public void testSolution() throws ExecutionException, InterruptedException, TimeoutException { 14 | CompletableFuture f = new Task().firstOf(new FutureFailingWhenBlocked<>(), CompletableFuture.completedFuture(1)); 15 | assertThat(f.get(1, TimeUnit.SECONDS)).isEqualTo(1); 16 | } 17 | 18 | @Test 19 | public void testSolution2() throws ExecutionException, InterruptedException, TimeoutException { 20 | CompletableFuture f = new Task().firstOf(CompletableFuture.completedFuture(2), new FutureFailingWhenBlocked<>()); 21 | assertThat(f.get(1, TimeUnit.SECONDS)).isEqualTo(2); 22 | } 23 | 24 | @Test 25 | public void doesNotBlock() throws ExecutionException, InterruptedException, TimeoutException { 26 | CompletableFuture f = new Task().firstOf(new FutureFailingWhenBlocked<>(), new FutureFailingWhenBlocked<>()); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Wait for all 3 | - Wait for first 4 | -------------------------------------------------------------------------------- /CompletableFuture/Combining futures/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1867976541 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Complete on callback/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.function.Consumer; 3 | 4 | public class Task { 5 | 6 | CompletableFuture create(MessageListener listener) { 7 | throw new UnsupportedOperationException("TODO"); 8 | } 9 | } 10 | 11 | @FunctionalInterface 12 | interface MessageListener { 13 | void onMessage(Consumer consumer); 14 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Complete on callback/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Complete on callback/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 2046189202 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Complete on callback/task.md: -------------------------------------------------------------------------------- 1 | Create a `CompletableFuture` that completes when `onMessage` callback is invoked. 2 | Notice that in this exercise `CompletableFuture` is not tied to any particular thread. 3 | 4 |
5 | Simply crate `CompletableFuture` using a default constructor. 6 |
7 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Complete on callback/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.atomic.AtomicReference; 3 | import java.util.function.Consumer; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class Tests { 10 | 11 | public static final String HELLO = "Hello!"; 12 | 13 | @Test 14 | public void testSolution() { 15 | AtomicReference> callback = new AtomicReference<>(); 16 | MessageListener listener = callback::set; 17 | CompletableFuture future = new Task().create(listener); 18 | assertThat(callback.get()) 19 | .describedAs("User defined callback for onMessage") 20 | .isNotNull(); 21 | assertThat(future.getNow(null)).isNull(); 22 | 23 | callback.get().accept(HELLO); 24 | assertThat(future.getNow(null)) 25 | .describedAs("Value of the future") 26 | .isEqualTo(HELLO); 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Create in specified thread pool/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.ExecutorService; 3 | import java.util.function.Supplier; 4 | 5 | public class Task { 6 | 7 | CompletableFuture async(Supplier supplier, ExecutorService executorService) { 8 | throw new UnsupportedOperationException("TODO"); 9 | } 10 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Create in specified thread pool/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Create in specified thread pool/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 864555170 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Create in specified thread pool/task.md: -------------------------------------------------------------------------------- 1 | Create a new `CompletableFuture` running in a specified thread pool. -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Create in specified thread pool/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.ExecutionException; 3 | import java.util.concurrent.LinkedBlockingQueue; 4 | import java.util.concurrent.ThreadFactory; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.TimeoutException; 8 | 9 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | public class Tests { 15 | 16 | @Test 17 | public void testSolution() throws ExecutionException, InterruptedException, TimeoutException { 18 | ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("CF-Pool-%d").build(); 19 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 20 | 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), 21 | threadFactory); 22 | CompletableFuture future = new Task().async(() -> Thread.currentThread().getName(), threadPoolExecutor); 23 | assertThat(ConcurrentAssertions.waitFor(future)).startsWith("CF-Pool-"); 24 | } 25 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Creating in default pool/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.function.Supplier; 3 | 4 | public class Task { 5 | 6 | CompletableFuture async(Supplier supplier) { 7 | throw new UnsupportedOperationException("TODO"); 8 | } 9 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Creating in default pool/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Creating in default pool/task.md: -------------------------------------------------------------------------------- 1 | Create a `CompletableFuture` that runs a task. 2 | Use a default thread pool for that. 3 | 4 |
5 | User `CompletableFuture.supplyAsync()` 6 |
7 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/Creating in default pool/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | 3 | import org.assertj.core.api.WithAssertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class Tests implements WithAssertions { 7 | 8 | @Test 9 | public void testSolution() { 10 | CompletableFuture future = new Task().async(() -> Thread.currentThread().getName()); 11 | 12 | assertThat(ConcurrentAssertions.waitFor(future)).startsWith("ForkJoinPool.commonPool-"); 13 | } 14 | } -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Creating in default pool 3 | - Create in specified thread pool 4 | - Complete on callback 5 | -------------------------------------------------------------------------------- /CompletableFuture/Creating futures/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 842604374 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Chaining/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.math.BigDecimal; 2 | import java.sql.ResultSet; 3 | import java.time.LocalDate; 4 | import java.util.concurrent.CompletableFuture; 5 | 6 | public abstract class Task { 7 | 8 | CompletableFuture todo(LocalDate date) throws Exception{ 9 | throw new UnsupportedOperationException("TODO"); 10 | } 11 | 12 | abstract CompletableFuture loadFromDb(LocalDate date); 13 | 14 | abstract CompletableFuture extract(ResultSet rs); 15 | 16 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Chaining/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Chaining/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1445661855 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Chaining/task.md: -------------------------------------------------------------------------------- 1 | When `loadFromDb()` completes, invoke `parse()` on its result. 2 | Notice that the result should be an asynchronous `CompletableFuture` as well. 3 | Keep in mind that when working with `CompletableFuture`, blocking operations should be used as a last resort only. 4 | 5 |
6 | 7 | Use `CompletableFuture.thenCompose()`. 8 | 9 |
-------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Chaining/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.math.BigDecimal; 2 | import java.sql.ResultSet; 3 | import java.time.LocalDate; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.assertj.core.api.Assertions; 8 | import org.junit.jupiter.params.ParameterizedTest; 9 | import org.junit.jupiter.params.provider.ValueSource; 10 | import org.mockito.Mockito; 11 | 12 | public class Tests { 13 | 14 | private static final LocalDate DATE = LocalDate.MIN; 15 | 16 | @ParameterizedTest 17 | @ValueSource(ints = {0, 1, 10}) 18 | public void testSolution(int x) throws Exception { 19 | BigDecimal val = BigDecimal.valueOf(x); 20 | Task task = new MyTask(val); 21 | Assertions.assertThat(task.todo(DATE).get(1, TimeUnit.SECONDS)).isEqualTo(val); 22 | } 23 | 24 | private static class MyTask extends Task { 25 | 26 | private final BigDecimal result; 27 | 28 | private MyTask(BigDecimal result) { 29 | this.result = result; 30 | } 31 | 32 | @Override 33 | CompletableFuture loadFromDb(LocalDate date) { 34 | return new FutureFailingWhenBlocked<>(Mockito.mock(ResultSet.class)); 35 | } 36 | 37 | @Override 38 | CompletableFuture extract(ResultSet rs) { 39 | return new FutureFailingWhenBlocked<>(result); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Timeouts/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CompletableFuture; 3 | 4 | public class Task { 5 | 6 | CompletableFuture applyTimeout(CompletableFuture original, Duration timeout) { 7 | throw new UnsupportedOperationException("TODO"); 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Timeouts/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests8.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Timeouts/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 501402118 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Timeouts/task.md: -------------------------------------------------------------------------------- 1 | When a `CompletableFuture` takes a long time to complete, time it out to avoid waiting forever. 2 | 3 |
4 | Check out `CompletableFuture.orTimeout()`. 5 |
6 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Timeouts/test/Tests8.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CompletableFuture; 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.concurrent.TimeoutException; 6 | 7 | import org.assertj.core.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | public class Tests8 { 13 | 14 | public static final int COMPLETION_AFTER = 200; 15 | 16 | @Test 17 | public void testSolution() throws Exception { 18 | CompletableFuture future = new Task().applyTimeout(new CompletableFuture<>(), Duration.ofMillis(10)); 19 | try { 20 | future.get(10 + 100, TimeUnit.MILLISECONDS); 21 | Assertions.failBecauseExceptionWasNotThrown(TimeoutException.class); 22 | } catch (TimeoutException e) { 23 | //expected 24 | } catch (ExecutionException e) { 25 | assertThat(e).hasCauseInstanceOf(TimeoutException.class); 26 | //expected 27 | } 28 | 29 | } 30 | 31 | @Test 32 | public void testTimeoutIsNotTooShort() throws Exception { 33 | int expected = 42; 34 | CompletableFuture future = CompletableFuture.supplyAsync(() -> { 35 | try { 36 | TimeUnit.MILLISECONDS.sleep(COMPLETION_AFTER); 37 | return expected; 38 | } catch (InterruptedException e) { 39 | throw new RuntimeException(e); 40 | } 41 | }); 42 | CompletableFuture withTimeout = new Task().applyTimeout(future, Duration.ofMillis(COMPLETION_AFTER * 2)); 43 | Integer result = withTimeout.get(COMPLETION_AFTER * 2, TimeUnit.MILLISECONDS); 44 | assertThat(result).isEqualTo(expected); 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Transforming/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | 3 | public class Task { 4 | 5 | CompletableFuture transform(CompletableFuture future) { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Transforming/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Transforming/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 634721671 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Transforming/task.md: -------------------------------------------------------------------------------- 1 | Transform the incoming `CompletableFuture` by parsing `String` to `Integer`. 2 | 3 |
4 | 5 | Use `CompletableFuture.thenApply()`. 6 | 7 |
-------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/Transforming/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.ExecutionException; 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.CsvSource; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | public class Tests { 12 | 13 | @ParameterizedTest 14 | @CsvSource({ 15 | "0, 0", 16 | "42, 42", 17 | "-1, -1" 18 | }) 19 | public void testSolution(String s, int i) throws ExecutionException, InterruptedException, TimeoutException { 20 | Task task = new Task(); 21 | CompletableFuture future = CompletableFuture.completedFuture(s); 22 | assertThat(task.transform(future).get(1, TimeUnit.SECONDS)).isEqualTo(i); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Transforming 3 | - Chaining 4 | - Timeouts 5 | -------------------------------------------------------------------------------- /CompletableFuture/Transforming futures/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1734063427 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /CompletableFuture/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Creating futures 3 | - Transforming futures 4 | - Combining futures 5 | -------------------------------------------------------------------------------- /CompletableFuture/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 220388397 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Arbitrary references/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.Optional; 2 | import java.util.concurrent.atomic.AtomicReference; 3 | 4 | public abstract class Task { 5 | 6 | AtomicReference singleton = new AtomicReference(); 7 | 8 | public Singleton getOrCreate() { 9 | return singleton.updateAndGet(cur -> cur == null ? create() : cur); 10 | } 11 | 12 | protected abstract Singleton create(); 13 | 14 | public Optional get() { 15 | return Optional.ofNullable(singleton.get()); 16 | } 17 | 18 | } 19 | 20 | interface Singleton { } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Arbitrary references/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Arbitrary references/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1412541050 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Arbitrary references/task.md: -------------------------------------------------------------------------------- 1 | # `AtomicReference` 2 | 3 | **Warning**: this task is tricky. 4 | 5 | Using `AtomicReference` try to implement a singleton pattern. 6 | `Singleton` should be created lazily and only once. 7 | Even if multiple threads are calling `getOrCreate` method concurrently. 8 | 9 |
10 | This is actual impossible with `AtomicReference` alone, without any extra synchronization (!) 11 |
12 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Arbitrary references/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicInteger; 2 | import java.util.stream.IntStream; 3 | 4 | import org.assertj.core.api.WithAssertions; 5 | import org.junit.jupiter.api.RepeatedTest; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests implements WithAssertions { 9 | 10 | Singleton instance = new Singleton() {}; 11 | 12 | @Test 13 | public void smoke() { 14 | AtomicInteger invocationCounter = new AtomicInteger(); 15 | Task task = new Task() { 16 | @Override 17 | protected Singleton create() { 18 | invocationCounter.incrementAndGet(); 19 | Thread.yield(); 20 | return instance; 21 | } 22 | }; 23 | assertThat(invocationCounter).hasValue(0); 24 | assertThat(task.get()).isEmpty(); 25 | 26 | assertThat(task.getOrCreate()).isEqualTo(instance); 27 | assertThat(invocationCounter).hasValue(1); 28 | 29 | assertThat(task.getOrCreate()).isEqualTo(instance); 30 | assertThat(invocationCounter).hasValue(1); 31 | 32 | } 33 | 34 | @RepeatedTest(10_000) 35 | public void multiThreadTest() { 36 | AtomicInteger invocationCounter = new AtomicInteger(); 37 | Task task = new Task() { 38 | @Override 39 | protected Singleton create() { 40 | invocationCounter.incrementAndGet(); 41 | Thread.yield(); 42 | return instance; 43 | } 44 | }; 45 | assertThat(invocationCounter).hasValue(0); 46 | assertThat(task.get()).isEmpty(); 47 | 48 | IntStream 49 | .range(0, 100) 50 | .parallel() 51 | .forEach(i -> assertThat(task.getOrCreate()).isEqualTo(instance)); 52 | assertThat(invocationCounter).hasValue(1); 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Boolean flag/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicBoolean; 2 | 3 | public class Task { 4 | 5 | private final AtomicBoolean lock = new AtomicBoolean(false); 6 | 7 | boolean tryLock() { 8 | throw new UnsupportedOperationException("TODO"); 9 | } 10 | 11 | boolean tryUnlock() { 12 | throw new UnsupportedOperationException("TODO"); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Boolean flag/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests10.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Boolean flag/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 88189963 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Boolean flag/task.md: -------------------------------------------------------------------------------- 1 | # `AtomicBoolean` 2 | 3 | The biggest benefit of `AtomicLong` is changing the value of the variable atomically, based on condition. 4 | I.e. you can flip it from `true` to `false` or vice versa without loosing any updates. 5 | Technically, you can implement critical section using `AtomicBoolean` as well. 6 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Boolean flag/test/Tests10.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | import java.util.concurrent.Semaphore; 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | import java.util.stream.Stream; 5 | 6 | import org.assertj.core.api.WithAssertions; 7 | import org.junit.jupiter.api.RepeatedTest; 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class Tests10 implements WithAssertions { 11 | 12 | @Test 13 | public void smokeTest() { 14 | //given 15 | Task task = new Task(); 16 | 17 | //expect 18 | assertThat(task.tryLock()).isEqualTo(true); 19 | assertThat(task.tryLock()).isEqualTo(false); 20 | assertThat(task.tryLock()).isEqualTo(false); 21 | 22 | //unlocking 23 | assertThat(task.tryUnlock()).isEqualTo(true); 24 | assertThat(task.tryUnlock()).isEqualTo(false); 25 | assertThat(task.tryUnlock()).isEqualTo(false); 26 | } 27 | 28 | @RepeatedTest(10) 29 | public void multiThreadedTest() { 30 | Task task = new Task(); 31 | Random random = new Random(); 32 | Semaphore onlyOneLock = new Semaphore(1); 33 | Events events = new Events<>(); 34 | AtomicInteger lockOwnersCount = new AtomicInteger(); 35 | Stream.generate(() -> (Runnable) () -> { 36 | if (random.nextBoolean()) { 37 | if (task.tryLock()) { 38 | lockOwnersCount.incrementAndGet(); 39 | } 40 | } else { 41 | if (task.tryUnlock()) { 42 | lockOwnersCount.decrementAndGet(); 43 | } 44 | } 45 | }) 46 | .limit(2_000_000) 47 | .parallel() 48 | .forEach(Runnable::run); 49 | assertThat(lockOwnersCount.get()).isBetween(0, 1); 50 | } 51 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Changing the value/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicLong; 2 | 3 | public class Task { 4 | 5 | void next(AtomicLong in) { 6 | in.set(collatz(in.get())); 7 | } 8 | 9 | private long collatz(long in) { 10 | if (in % 2 == 0) { 11 | return in / 2; 12 | } else { 13 | return 3 * in + 1; 14 | } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Changing the value/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Changing the value/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 79276198 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Changing the value/task.md: -------------------------------------------------------------------------------- 1 | # Changing value atomically 2 | 3 | Consider the following integer: 4 | 5 | ```java 6 | AtomicInteger atom = new AtomicInteger(0); 7 | ``` 8 | 9 | This is not thread safe (lost update): 10 | 11 | ```java 12 | atom.set(atom.get() * 2); 13 | ``` 14 | This is thread safe, but annoying: 15 | 16 | ```java 17 | int current; 18 | do { 19 | current = atom.get(); 20 | } while(!atom.compareAndSet(current, current * 2)); 21 | ``` 22 | 23 | # Your task 24 | 25 | Rewrite the `next()` method in such a way that it works even when invoked from multiple threads. 26 | Can you write it as one-liner? 27 | 28 |
29 | See `AtomicInteger.updateAndGet()`. 30 |
31 | 32 | 33 | Tests in this exercise use [Collatz conjecture](https://en.wikipedia.org/wiki/Collatz_conjecture) formula. -------------------------------------------------------------------------------- /Data structures/Atomic classes/Changing the value/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicLong; 2 | import java.util.stream.IntStream; 3 | 4 | import org.assertj.core.api.WithAssertions; 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.CsvSource; 7 | 8 | public class Tests implements WithAssertions { 9 | 10 | private static final String INPUT = 11 | "9, 19\n" + 12 | "97, 118\n" + 13 | "871, 178\n" + 14 | "6171, 261\n" + 15 | "77031, 350\n" + 16 | "837799, 524\n" + 17 | "8400511, 685\n" + 18 | "63728127, 949\n" + 19 | "670617279, 986\n" + 20 | "9780657630, 1132\n" + 21 | "75128138247, 1228\n" + 22 | "989345275647, 1348\n"; 23 | 24 | @ParameterizedTest(name = "single threaded: {argumentsWithNames}") 25 | @CsvSource(textBlock = INPUT) 26 | public void singleThreaded(long start, int steps) { 27 | AtomicLong in = new AtomicLong(start); 28 | Task task = new Task(); 29 | IntStream.range(0, steps).forEach(i -> task.next(in)); 30 | assertThat(in).hasValue(1); 31 | } 32 | 33 | @ParameterizedTest(name = "multi threaded: {argumentsWithNames}") 34 | @CsvSource(textBlock = INPUT) 35 | public void multiThreaded(long start, int steps) { 36 | AtomicLong in = new AtomicLong(start); 37 | Task task = new Task(); 38 | IntStream.range(0, steps).parallel().forEach(i -> task.next(in)); 39 | assertThat(in).hasValue(1); 40 | } 41 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Incrementing counter/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | //put your task here 3 | } -------------------------------------------------------------------------------- /Data structures/Atomic classes/Incrementing counter/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Incrementing counter/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 752333082 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/Incrementing counter/task.md: -------------------------------------------------------------------------------- 1 | # `AtomicInteger.incrementAndGet()` 2 | 3 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Boolean flag 3 | - Incrementing counter 4 | - Changing the value 5 | - Arbitrary references 6 | -------------------------------------------------------------------------------- /Data structures/Atomic classes/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1553491203 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Data structures/Maps/Atomic transformation/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.Map; 2 | 3 | public class Task { 4 | 5 | Map createScoresMap() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | void applyScore(Map scores, String player, int points) { 10 | throw new UnsupportedOperationException("TODO"); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Data structures/Maps/Atomic transformation/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Data structures/Maps/Atomic transformation/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 18164332 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Maps/Atomic transformation/task.md: -------------------------------------------------------------------------------- 1 | There's a `scores` `Map` that holds the number of points per each player. 2 | Implement two methods: 3 | 4 | * `createScoresMap()` that creates a new `Map scores` instance - it must be thread-safe! 5 | * `applyScore(String player, int points)` that adds a new player with the given number of points to the `Map`. `points` can be negative 6 | 7 | Keep in mind that when previously unseen `player` appears, it should be added to the map. 8 | If it was previously seen, the number of points should be incremented/decremented. 9 | 10 |
11 | 12 | Keep in mind that even if `get()` and `put()` (or even `putIfAbsent()`) methods are individually thread-safe, multiple calls are not "transactional". 13 | 14 |
-------------------------------------------------------------------------------- /Data structures/Maps/Atomic transformation/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.Collections; 2 | import java.util.HashSet; 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.IntStream; 7 | 8 | import org.junit.jupiter.api.RepeatedTest; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import static java.util.Map.entry; 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | public class Tests { 15 | 16 | public static final int PLAYERS = 50000; 17 | public static final int SCORE_RANGE = 100; 18 | private final Task task = new Task(); 19 | 20 | @Test 21 | public void smoke() { 22 | Map scores = task.createScoresMap(); 23 | task.applyScore(scores, "a", 1); 24 | task.applyScore(scores, "b", 2); 25 | task.applyScore(scores, "a", 3); 26 | assertThat(scores).containsExactly( 27 | entry("a", 4), 28 | entry("b", 2) 29 | ); 30 | } 31 | 32 | @RepeatedTest(value = 10, name = "tonOfConcurrentEvents {currentRepetition} of {totalRepetitions}") 33 | public void tonOfConcurrentEvents() { 34 | Map scores = task.createScoresMap(); 35 | List> actions = IntStream.range(0, PLAYERS) 36 | .mapToObj(Integer::toString) 37 | .flatMap(player -> IntStream.rangeClosed(-SCORE_RANGE, SCORE_RANGE).mapToObj(i -> entry(player, i))) 38 | .collect(Collectors.toList()); 39 | Collections.shuffle(actions); 40 | actions 41 | .parallelStream() 42 | .forEach(e -> task.applyScore(scores, e.getKey(), e.getValue())); 43 | assertThat(scores) 44 | .hasSize(PLAYERS); 45 | assertThat(new HashSet<>(scores.values())).isEqualTo(new HashSet<>(List.of(0))); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /Data structures/Maps/Concurrent maps overview/src/Main.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ConcurrentHashMap; 2 | import java.util.concurrent.ConcurrentMap; 3 | 4 | public class Main { 5 | 6 | public static void main(String[] args) { 7 | ConcurrentMap map = new ConcurrentHashMap<>(); 8 | if(!map.containsKey("key")) { 9 | map.put("key", 1); 10 | } else { 11 | map.put("key", map.get("key") + 1); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Data structures/Maps/Concurrent maps overview/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Main.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Data structures/Maps/Concurrent maps overview/task.md: -------------------------------------------------------------------------------- 1 | # `ConcurrentHashMap` 2 | 3 | Normal `HashMap` is not thread safe. 4 | All operations can make map inconsistent. 5 | Individual operations on `ConcurrentHashMap` are thread safe. 6 | However, individual operations on `ConcurrentHashMap` are not atomic. 7 | For example, this is broken. 8 | Can you tell why? 9 | 10 | ```java 11 | ConcurrentMap map = new ConcurrentHashMap<>(); 12 | if(!map.containsKey("key")) { 13 | map.put("key", 1); 14 | } else { 15 | map.put("key", map.get("key") + 1); 16 | } 17 | ``` 18 | 19 | # More materials 20 | 21 | * [Why accessing Java HashMap may cause infinite loop in concurrent environment](https://www.pixelstech.net/article/1585457836-Why-Java-HashMap-is-considered-as-thread-unsafe) 22 | * [Java HashMap.get(Object) infinite loop](https://stackoverflow.com/questions/35534906/java-hashmap-getobject-infinite-loop) -------------------------------------------------------------------------------- /Data structures/Maps/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Concurrent maps overview 3 | - Atomic transformation 4 | -------------------------------------------------------------------------------- /Data structures/Maps/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 919388801 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/src/Job.java: -------------------------------------------------------------------------------- 1 | @FunctionalInterface 2 | interface Job extends Runnable { 3 | 4 | 5 | } 6 | -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.BlockingQueue; 2 | 3 | public class Task { 4 | 5 | void acceptJobs(BlockingQueue jobs) throws InterruptedException { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | - name: src/Job.java 8 | visible: true 9 | - name: test/TestJob.java 10 | visible: true 11 | -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1436311129 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/task.md: -------------------------------------------------------------------------------- 1 | Implement a consumer that fetches and runs `Job`s from a queue. 2 | When there are no jobs to run, it should wait indefinitely. 3 | When consumer is interrupted, it should rethrow `InterrupedException` and stop. 4 | 5 |
6 | Use `BlockingQueue.take()` in a loop. 7 |
-------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/test/TestJob.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | 3 | public class TestJob implements Job { 4 | 5 | final CountDownLatch run = new CountDownLatch(1); 6 | 7 | @Override 8 | public void run() { 9 | run.countDown(); 10 | } 11 | 12 | void waitUntilRun() throws InterruptedException { 13 | ConcurrentAssertions.waitFor(run, "job was run"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Data structures/Queues/Basic consumer/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ArrayBlockingQueue; 2 | import java.util.concurrent.BlockingQueue; 3 | import java.util.stream.IntStream; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class Tests { 8 | 9 | private final TestJob[] jobs = new TestJob[] { 10 | new TestJob(), 11 | new TestJob() 12 | }; 13 | 14 | @Test 15 | public void shouldRunNoJobsAndSupportInterruption() throws InterruptedException { 16 | Thread thread = runInSeparateThread(0); 17 | thread.start(); 18 | thread.interrupt(); 19 | } 20 | 21 | @Test 22 | public void shouldRunJob() throws InterruptedException { 23 | Thread thread = runInSeparateThread(1); 24 | thread.start(); 25 | jobs[0].waitUntilRun(); 26 | thread.interrupt(); 27 | } 28 | 29 | @Test 30 | public void shouldRunTwoJobs() throws InterruptedException { 31 | Thread thread = runInSeparateThread(2); 32 | thread.start(); 33 | jobs[0].waitUntilRun(); 34 | jobs[1].waitUntilRun(); 35 | thread.interrupt(); 36 | } 37 | 38 | private Thread runInSeparateThread(int howManyTasks) throws InterruptedException { 39 | return new Thread(() -> { 40 | try { 41 | Task task = new Task(); 42 | BlockingQueue jobs = new ArrayBlockingQueue<>(100); 43 | IntStream.range(0, howManyTasks).forEach(i -> jobs.add(this.jobs[i])); 44 | task.acceptJobs(jobs); 45 | } catch (InterruptedException e) { 46 | Thread.currentThread().interrupt(); 47 | } 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Data structures/Queues/Ordered concurrency/src/CustomerJob.java: -------------------------------------------------------------------------------- 1 | public class CustomerJob implements Runnable { 2 | 3 | private final int key; 4 | private final Runnable target; 5 | 6 | public CustomerJob(int key, Runnable target) { 7 | this.key = key; 8 | this.target = target; 9 | } 10 | 11 | @Override 12 | public void run() { 13 | target.run(); 14 | } 15 | 16 | public int getKey() { 17 | return key; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Data structures/Queues/Ordered concurrency/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.BlockingQueue; 2 | 3 | public class Task { 4 | 5 | void consume(BlockingQueue queue) throws Exception { 6 | } 7 | } -------------------------------------------------------------------------------- /Data structures/Queues/Ordered concurrency/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: src/CustomerJob.java 6 | visible: true 7 | - name: test/Tests11.java 8 | visible: true 9 | -------------------------------------------------------------------------------- /Data structures/Queues/Ordered concurrency/task.md: -------------------------------------------------------------------------------- 1 | When there's just a single consumer of a queue, jobs are run sequentially, not concurrently. 2 | Thus, if two jobs are related to the same resource (e.g. client), they should be run in sequence. 3 | However, if two jobs are unrelated (e.g. two different clients), they can be run concurrently. 4 | This can improve throughput of the system without breaking the consistency of the data. 5 | 6 | # Task 7 | 8 | Your `CustomerJob` jobs have an `int key` property. 9 | Jobs with the same key cannot run concurrently. 10 | But jobs with different `key` are free to run at the same time. 11 | For example, `key=1` and `key=2` _should_ run concurrently. 12 | How do you implement such a consumer? 13 | Your implementation should support at least *10* concurrently running `CustomerJob`s. 14 | But jobs for the same key should be serialized. 15 | 16 |
17 | Imagine you have a separate thread for each unique key. 18 | However, this doesn't scale. 19 |
20 | -------------------------------------------------------------------------------- /Data structures/Queues/Ordered concurrency/test/Tests11.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.time.Duration; 3 | import java.util.List; 4 | import java.util.concurrent.ArrayBlockingQueue; 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.Semaphore; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.IntStream; 11 | 12 | import org.assertj.core.api.WithAssertions; 13 | import org.junit.jupiter.api.Disabled; 14 | import org.junit.jupiter.api.RepeatedTest; 15 | import org.junit.jupiter.api.Test; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | public class Tests11 implements WithAssertions { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 22 | 23 | private final BlockingQueue queue = new ArrayBlockingQueue<>(1000000); 24 | private final Events events = new Events<>(); 25 | 26 | @Test 27 | public void singleTaskIsConsumed() throws Exception { 28 | CountDownLatch waiting = new CountDownLatch(1); 29 | queue.add(new CustomerJob(1, () -> { 30 | Sleeper.sleepRandomly(Duration.ofMillis(100)); 31 | waiting.countDown(); 32 | })); 33 | consumeAsync(); 34 | ConcurrentAssertions.waitFor(waiting, "task to finish"); 35 | } 36 | 37 | @Test 38 | public void twoTasksWithDifferentKeyMustRunConcurrently() { 39 | CountDownLatch startLatch = new CountDownLatch(2); 40 | CountDownLatch completeLatch = new CountDownLatch(2); 41 | queue.add(new CustomerJob(1, task2(startLatch, completeLatch))); 42 | queue.add(new CustomerJob(2, task2(startLatch, completeLatch))); 43 | consumeAsync(); 44 | ConcurrentAssertions.waitFor(completeLatch, "tasks to finish concurrently"); 45 | } 46 | 47 | @RepeatedTest(10) 48 | @Disabled 49 | public void randomizedTest() { 50 | List semaphores = IntStream.range(0, 10).mapToObj(x -> new Semaphore(1)).collect(Collectors.toList()); 51 | final int KEYS = 10; 52 | final int TOTAL = 10_000; 53 | CountDownLatch total = new CountDownLatch(TOTAL); 54 | IntStream.range(0, TOTAL).mapToObj(x -> new CustomerJob(x % KEYS, () -> { 55 | try { 56 | Semaphore semaphore = semaphores.get(x % KEYS); 57 | ConcurrentAssertions.tryAcquire(semaphore, "jobs running concurrently with key " + x % KEYS); 58 | try { 59 | Sleeper.sleepRandomly(Duration.ofMillis(1)); 60 | total.countDown(); 61 | } finally { 62 | semaphore.release(); 63 | } 64 | } catch (Exception e) { 65 | events.log(e.toString()); 66 | } 67 | })) 68 | .parallel() 69 | .forEach(queue::add); 70 | consumeAsync(); 71 | ConcurrentAssertions.waitFor(total, "All " + TOTAL + " tasks to finish", Duration.ofSeconds(30)); 72 | assertThat(events.drainToList()).isEmpty(); 73 | } 74 | 75 | private Runnable task2(CountDownLatch startLatch, CountDownLatch completeLatch) { 76 | return () -> { 77 | try { 78 | startLatch.countDown(); 79 | ConcurrentAssertions.waitFor(startLatch, "all tasks to start", Duration.ofSeconds(2)); 80 | events.log("Started"); 81 | Sleeper.sleepRandomly(Duration.ofMillis(100)); 82 | events.log("Done"); 83 | completeLatch.countDown(); 84 | } catch (Exception e) { 85 | events.log(e.toString()); 86 | } 87 | }; 88 | } 89 | 90 | @Test 91 | public void twoTasksWithSameKeyCantRunConcurrently() { 92 | Semaphore noConcurrency = new Semaphore(1); 93 | CountDownLatch waiting = new CountDownLatch(2); 94 | queue.add(new CustomerJob(1, () -> task(noConcurrency, waiting))); 95 | queue.add(new CustomerJob(1, () -> task(noConcurrency, waiting))); 96 | consumeAsync(); 97 | ConcurrentAssertions.waitFor(waiting, "tasks to finish concurrently"); 98 | } 99 | 100 | private void task(Semaphore noConcurrency, CountDownLatch waiting) { 101 | ConcurrentAssertions.tryAcquire(noConcurrency, "running CustomerJobs concurrently"); 102 | try { 103 | Sleeper.sleepRandomly(Duration.ofMillis(100)); 104 | events.log("Done"); 105 | } finally { 106 | noConcurrency.release(); 107 | } 108 | waiting.countDown(); 109 | } 110 | 111 | private void consumeAsync() { 112 | CompletableFuture.supplyAsync(() -> { 113 | try { 114 | new Task().consume(queue); 115 | } catch (Exception e) { 116 | log.error("Opps", e); 117 | } 118 | return null; 119 | }); 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /Data structures/Queues/Single consumer/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.BlockingQueue; 2 | 3 | public class Task { 4 | 5 | void consume(BlockingQueue queue) { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /Data structures/Queues/Single consumer/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests9.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Data structures/Queues/Single consumer/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1087274501 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Data structures/Queues/Single consumer/task.md: -------------------------------------------------------------------------------- 1 | When there's just a single consumer of a queue, jobs are run sequentially, not concurrently. 2 | Consume the incoming queue of jobs in such a way that two jobs never run concurrently. 3 | Do not use any locking/synchronization mechanism. 4 | 5 | This method should be asynchronous, i.e. should consume queue in the background. 6 | 7 |
8 | Jobs can run concurrently only if more than two threads are processing the same queue. 9 |
10 | -------------------------------------------------------------------------------- /Data structures/Queues/Single consumer/test/Tests9.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.util.concurrent.ArrayBlockingQueue; 3 | import java.util.concurrent.BlockingQueue; 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.Semaphore; 6 | import java.util.stream.IntStream; 7 | 8 | import org.assertj.core.api.WithAssertions; 9 | import org.junit.jupiter.api.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | public class Tests9 implements WithAssertions { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 16 | public static final int TOTAL_TASKS = 4; 17 | 18 | private final Events events = new Events<>(); 19 | private final Events errors = new Events<>(); 20 | 21 | @Test 22 | public void testSolution() { 23 | BlockingQueue queue = new ArrayBlockingQueue<>(100); 24 | Semaphore concurrencyControl = new Semaphore(1); 25 | new Task().consume(queue); 26 | 27 | CountDownLatch waitForAll = new CountDownLatch(1); 28 | CountDownLatch howManyPending = new CountDownLatch(TOTAL_TASKS); 29 | IntStream.rangeClosed(1, TOTAL_TASKS).forEach(i -> 30 | queue.add(ErrorCollecting.collectErrors(errors, () -> { 31 | events.log("START " + i); 32 | ConcurrentAssertions.tryAcquire(concurrencyControl, "concurrent jobs being processed"); 33 | try { 34 | ConcurrentAssertions.waitFor(waitForAll, "all jobs to be processed"); 35 | } finally { 36 | concurrencyControl.release(); 37 | events.log("STOP " + i); 38 | howManyPending.countDown(); 39 | } 40 | }))); 41 | 42 | waitForAll.countDown(); 43 | ConcurrentAssertions.waitFor(howManyPending, "all jobs to terminate"); 44 | assertThat(errors.drainToList()).isEmpty(); 45 | assertThat(events.drainToList()).containsExactly( 46 | "START 1", "STOP 1", 47 | "START 2", "STOP 2", 48 | "START 3", "STOP 3", 49 | "START 4", "STOP 4" 50 | ); 51 | } 52 | } -------------------------------------------------------------------------------- /Data structures/Queues/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Basic consumer 3 | - Single consumer 4 | - Ordered concurrency 5 | -------------------------------------------------------------------------------- /Data structures/Queues/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1458563595 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Data structures/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Queues 3 | - Maps 4 | - Atomic classes 5 | -------------------------------------------------------------------------------- /Data structures/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 516280834 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/CPU basics/src/Main.java: -------------------------------------------------------------------------------- 1 | public class Main { 2 | } -------------------------------------------------------------------------------- /Introduction/Computer architecture/CPU basics/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Main.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/CPU basics/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 384521936 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/CPU basics/task.md: -------------------------------------------------------------------------------- 1 | # CPU 2 | 3 | * Runs code. 4 | * Can have multiple cores. 5 | * CPU has many layers of caches. 6 | 7 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/Terms and definitions/src/Main.java: -------------------------------------------------------------------------------- 1 | public class Main { 2 | public static void main(String[] args) { 3 | // Write your solution here 4 | } 5 | } -------------------------------------------------------------------------------- /Introduction/Computer architecture/Terms and definitions/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Main.java 4 | visible: true 5 | - name: tweet.png 6 | visible: true 7 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/Terms and definitions/task.md: -------------------------------------------------------------------------------- 1 | # Thread 2 | 3 | A running thread needs CPU core. 4 | When a thread is blocked or waiting for I/O, it releases a CPU. 5 | 6 | ![img.png](tweet.png) 7 | 8 | [twitter.com/adamgordonbell/status/1467229754029969408](https://twitter.com/adamgordonbell/status/1467229754029969408) 9 | 10 | # Process 11 | 12 | A group of threads that share the same memory. 13 | For Linux kernal, the distinction between a thread and a process is minimal. 14 | 15 | # Critical section 16 | 17 | A place in code that can be run only by a single thread. 18 | 19 | # Lock 20 | 21 | A resource/object that can be obtained by a single thread only. 22 | 23 | # Lost update 24 | 25 | A bad phenomenon where a thread updates a shared state and another thread reads it before the update is complete. 26 | -------------------------------------------------------------------------------- /Introduction/Computer architecture/Terms and definitions/tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurkiewicz/java-concurrency-workshop/65ab5d97bdabc00bdb919d60f74b0b1d872afdcd/Introduction/Computer architecture/Terms and definitions/tweet.png -------------------------------------------------------------------------------- /Introduction/Computer architecture/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - CPU basics 3 | - Terms and definitions -------------------------------------------------------------------------------- /Introduction/Computer architecture/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1607150949 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Introduction/Welcome/Hello World/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | 3 | int getAnswer() { 4 | return 42; 5 | } 6 | 7 | } -------------------------------------------------------------------------------- /Introduction/Welcome/Hello World/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Introduction/Welcome/Hello World/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1433267404 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Introduction/Welcome/Hello World/task.md: -------------------------------------------------------------------------------- 1 | # Welcome to Java Concurrency hands-on workshop! 2 | 3 | This first assignment should help you understand how this platform works. 4 | 5 | Make sure your can run the first task - it should pass. 6 | Simply click "_Check_" button and wait for green result. 7 | You can always skip any lesson if you want. 8 | 9 | ## About tests 10 | 11 | Keep in mind that I'm doing my best to cover your solution with tests. 12 | However, chances are your correct solution 13 | 14 |
15 | Often you are given a hint to help you pass a certain task. 16 |
17 | -------------------------------------------------------------------------------- /Introduction/Welcome/Hello World/test/Tests.java: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test; 2 | 3 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat; 4 | 5 | public class Tests { 6 | 7 | @Test 8 | public void testSolution() { 9 | assertThat(new Task().getAnswer()).isEqualTo(42); 10 | } 11 | } -------------------------------------------------------------------------------- /Introduction/Welcome/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Hello World 3 | -------------------------------------------------------------------------------- /Introduction/Welcome/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 389665637 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Introduction/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Welcome 3 | - Computer architecture 4 | -------------------------------------------------------------------------------- /Introduction/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 129981684 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/Guarantees/src/Main.java: -------------------------------------------------------------------------------- 1 | public class Main { 2 | public static void main(String[] args) { 3 | // Write your solution here 4 | } 5 | } -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/Guarantees/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Main.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/Guarantees/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 746823101 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/Guarantees/task.md: -------------------------------------------------------------------------------- 1 | # `volatile` 2 | 3 | # `synchronized` 4 | -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Guarantees 3 | -------------------------------------------------------------------------------- /Java Memory Model/Java Memory Model/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1897993792 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Java Memory Model/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Java Memory Model 3 | -------------------------------------------------------------------------------- /Java Memory Model/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 449514423 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Micrometer/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | 5 | public class Task { 6 | 7 | ExecutorService addMetrics(MeterRegistry meterRegistry, ExecutorService executor) { 8 | throw new UnsupportedOperationException("TODO"); 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/Micrometer/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Micrometer/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1115153206 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Micrometer/task.md: -------------------------------------------------------------------------------- 1 | Instrument existing `ExecutorService` using `io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics`. 2 | 3 | 4 | # Testing Graphite with Docker 5 | 6 | As a bonus you can try to run this container and configure Graphite integration. 7 | Once done, you should see graph of `ExecutorSerivce` metrics. 8 | 9 | ```bash 10 | docker run -d \ 11 | --name graphite\ 12 | -p 80:80\ 13 | -p 81:81\ 14 | -p 2003-2004:2003-2004\ 15 | -p 2023-2024:2023-2024\ 16 | -p 8125:8125/udp\ 17 | -p 8126:8126\ 18 | hopsoft/graphite-statsd 19 | ``` -------------------------------------------------------------------------------- /Monitoring/Thread pools/Micrometer/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.time.Duration; 3 | import java.util.Random; 4 | import java.util.Set; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.Semaphore; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.stream.Collectors; 10 | 11 | import io.micrometer.core.instrument.Clock; 12 | import io.micrometer.core.instrument.Meter; 13 | import io.micrometer.core.instrument.MeterRegistry; 14 | import io.micrometer.core.instrument.simple.SimpleMeterRegistry; 15 | import io.micrometer.graphite.GraphiteConfig; 16 | import io.micrometer.graphite.GraphiteMeterRegistry; 17 | import org.assertj.core.api.WithAssertions; 18 | import org.junit.jupiter.api.Disabled; 19 | import org.junit.jupiter.api.Test; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | public class Tests implements WithAssertions { 24 | 25 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 26 | 27 | @Test 28 | public void smokeTest() { 29 | SimpleMeterRegistry registry = new SimpleMeterRegistry(); 30 | new Task().addMetrics(registry, Executors.newFixedThreadPool(10)); 31 | 32 | Set metrics = registry 33 | .getMeters() 34 | .stream() 35 | .map(Meter::getId) 36 | .map(Meter.Id::getName) 37 | .collect(Collectors.toSet()); 38 | assertThat(metrics) 39 | .contains("executor.queued", "executor.active", "executor.pool.size"); 40 | } 41 | 42 | @Test 43 | @Disabled 44 | public void graphite() throws InterruptedException { 45 | MeterRegistry registry = new GraphiteMeterRegistry(new GraphiteConfig() { 46 | @Override 47 | public Duration step() { 48 | return Duration.ofSeconds(2); 49 | } 50 | 51 | @Override 52 | public String get(String k) { 53 | return null; 54 | } 55 | }, Clock.SYSTEM); 56 | ExecutorService pool = new Task().addMetrics(registry, Executors.newFixedThreadPool(10)); 57 | 58 | Set metrics = registry 59 | .getMeters() 60 | .stream() 61 | .map(Meter::getId) 62 | .map(Meter.Id::getName) 63 | .collect(Collectors.toSet()); 64 | assertThat(metrics) 65 | .contains("executor.queued", "executor.active", "executor.pool.size"); 66 | 67 | Random random = new Random(); 68 | Semaphore semaphore = new Semaphore(1); 69 | while (true) { 70 | semaphore.acquire(); 71 | pool.submit(() -> { 72 | log.info("Executing"); 73 | TimeUnit.MILLISECONDS.sleep((long) (500 + random.nextGaussian() * 200)); 74 | semaphore.release(); 75 | return 42; 76 | }); 77 | } 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/Queue size/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | //put your task here 3 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/Queue size/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Queue size/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1305891051 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Queue size/task.md: -------------------------------------------------------------------------------- 1 | # Monitoring queue size 2 | 3 | `ExecutorService` has two parts: 4 | 5 | * a pool of worker threads 6 | * a queue of tasks in front of the pool 7 | 8 | In a healthy system, tasks should not be queued extensively. -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/src/CallableWithTaskRuntime.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.time.Instant; 3 | import java.util.concurrent.Callable; 4 | 5 | class CallableWithTaskRuntime implements Callable { 6 | 7 | private final Callable target; 8 | private Instant started; 9 | private Instant completed; 10 | 11 | public CallableWithTaskRuntime(Callable target) { 12 | this.target = target; 13 | } 14 | 15 | public Duration getRuntime() { 16 | return Duration.between(started, completed); 17 | } 18 | 19 | @Override 20 | public T call() throws Exception { 21 | //TODO Implement here 22 | return target.call(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Callable; 2 | 3 | public class Task { 4 | 5 | CallableWithTaskRuntime instrument(Callable raw) { 6 | //TODO Implement CallableWithTaskRuntime 7 | return new CallableWithTaskRuntime<>(raw); 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | - name: src/CallableWithTaskRuntime.java 8 | visible: true 9 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 73612655 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/task.md: -------------------------------------------------------------------------------- 1 | Measure how much time a task runs is very valuable. 2 | If a certain task in a thread pool takes longer than usual, it can lead to starvation of other tasks. 3 | 4 | # Your task 5 | 6 | Implement a `Callable` that wraps the original `Callable` and returns the time it was executed. 7 | Most of the work is already done in `CallableWithTaskRuntime` class. 8 | We use [Decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern) here. -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task runtime/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.Callable; 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.assertj.core.api.WithAssertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests implements WithAssertions { 9 | 10 | @Test 11 | public void testSolution() throws Exception { 12 | Callable call = () -> { 13 | TimeUnit.MILLISECONDS.sleep(500); 14 | return null; 15 | }; 16 | CallableWithTaskRuntime instrumented = new Task().instrument(call); 17 | instrumented.call(); 18 | assertThat(instrumented.getRuntime()).isGreaterThanOrEqualTo(Duration.ofMillis(500)); 19 | } 20 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/src/CallableWithWaitTime.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.time.Instant; 3 | import java.util.concurrent.Callable; 4 | 5 | public class CallableWithWaitTime implements Callable { 6 | 7 | private final Instant created; 8 | private Callable target; 9 | private Instant started; 10 | 11 | public CallableWithWaitTime(Callable target) { 12 | this.created = Instant.now(); 13 | } 14 | 15 | Duration getIdleTime() { 16 | return Duration.between(created, started); 17 | } 18 | 19 | @Override 20 | public T call() throws Exception { 21 | //TODO Implement here 22 | return target.call(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Callable; 2 | 3 | public class Task { 4 | 5 | CallableWithWaitTime instrument(Callable raw) { 6 | //TODO Implement CallableWithWaitTime 7 | return new CallableWithWaitTime<>(raw); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | - name: src/CallableWithWaitTime.java 8 | visible: true 9 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1566057955 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/task.md: -------------------------------------------------------------------------------- 1 | Instrumenting a `Callable` to figure out how much time it spent in the queue is quite come. 2 | Ideally, the task shouldn't spend much time there and be executed immediately. 3 | If you consistently see tasks queueing up, consider tuning your system. 4 | 5 | # Task 6 | 7 | Implement a `Callable` that wraps the original `Callable` and returns the time spent in the queue. 8 | Most of the work is already done in `CallableWithWaitTime` class. 9 | We use [Decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern) here. -------------------------------------------------------------------------------- /Monitoring/Thread pools/Task waiting time/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.Callable; 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.assertj.core.api.WithAssertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests implements WithAssertions { 9 | 10 | @Test 11 | public void testSolution() throws Exception { 12 | Callable call = () -> null; 13 | CallableWithWaitTime instrumented = new Task().instrument(call); 14 | TimeUnit.MILLISECONDS.sleep(500); 15 | instrumented.call(); 16 | assertThat(instrumented.getIdleTime()).isGreaterThanOrEqualTo(Duration.ofMillis(500)); 17 | } 18 | } -------------------------------------------------------------------------------- /Monitoring/Thread pools/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Queue size 3 | - Task runtime 4 | - Task waiting time 5 | - Micrometer 6 | -------------------------------------------------------------------------------- /Monitoring/Thread pools/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1037673115 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Monitoring/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Thread pools 3 | -------------------------------------------------------------------------------- /Monitoring/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 503600794 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jave Concurrency Workshop 2 | 3 | Based on [IntelliJ EduTools plugin](https://plugins.jetbrains.com/plugin/10081-edutools). 4 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Multiple threads waiting/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.util.concurrent.CompletableFuture; 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.function.Supplier; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class Task { 11 | 12 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 13 | 14 | public static void main(String[] args) throws InterruptedException { 15 | CountDownLatch latch = new CountDownLatch(1); 16 | 17 | Supplier call = () -> { 18 | log.info("Starting"); 19 | try { 20 | latch.await(); 21 | log.info("Executing"); 22 | } catch (InterruptedException e) { 23 | log.warn("Interrupted", e); 24 | } 25 | return null; 26 | }; 27 | 28 | CompletableFuture.supplyAsync(call); 29 | TimeUnit.SECONDS.sleep(1); 30 | CompletableFuture.supplyAsync(call); 31 | TimeUnit.SECONDS.sleep(1); 32 | CompletableFuture.supplyAsync(call); 33 | TimeUnit.SECONDS.sleep(1); 34 | latch.countDown(); 35 | } 36 | 37 | 38 | 39 | } -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Multiple threads waiting/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Multiple threads waiting/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 831103995 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Multiple threads waiting/task.md: -------------------------------------------------------------------------------- 1 | Multiple threads are waiting to start at the same time. 2 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Wait for multiple threads to finish/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.time.Duration; 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.function.Supplier; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class Task { 12 | 13 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 14 | 15 | public static void main(String[] args) throws InterruptedException { 16 | CountDownLatch latch = new CountDownLatch(1); 17 | CountDownLatch waitForAll = new CountDownLatch(3); 18 | 19 | Supplier call = () -> { 20 | log.info("Starting"); 21 | try { 22 | latch.await(); 23 | log.info("Executing"); 24 | Sleeper.sleepRandomly(Duration.ofSeconds(5)); 25 | log.info("I'm done"); 26 | waitForAll.countDown(); 27 | } catch (InterruptedException e) { 28 | log.warn("Interrupted", e); 29 | } 30 | return null; 31 | }; 32 | 33 | CompletableFuture.supplyAsync(call); 34 | TimeUnit.SECONDS.sleep(1); 35 | CompletableFuture.supplyAsync(call); 36 | TimeUnit.SECONDS.sleep(1); 37 | CompletableFuture.supplyAsync(call); 38 | TimeUnit.SECONDS.sleep(1); 39 | latch.countDown(); 40 | waitForAll.await(); 41 | log.info("All done"); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Wait for multiple threads to finish/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Wait for multiple threads to finish/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1925616120 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/Wait for multiple threads to finish/task.md: -------------------------------------------------------------------------------- 1 | You can use `CountDownLatch` to wait for multiple tasks to finish. 2 | This way you can be sure all jobs completed before you begin. 3 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Multiple threads waiting 3 | - Wait for multiple threads to finish 4 | -------------------------------------------------------------------------------- /Synchronization/CountDownLatch/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 2010300595 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/ReentrantLock/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.locks.ReentrantLock; 2 | 3 | public class Task { 4 | 5 | public static void main(String[] args) { 6 | ReentrantLock lock = new ReentrantLock(); 7 | lock.lock(); 8 | try { 9 | //critical section 10 | } finally { 11 | lock.unlock(); 12 | } 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/ReentrantLock/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/ReentrantLock/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 541878282 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/ReentrantLock/task.md: -------------------------------------------------------------------------------- 1 | Use `ReentrantLock` to only allow one thread in *critical section*. -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/Synchronized/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | 3 | public static void main(String[] args) { 4 | synchronized(Task.class) { 5 | //Critical section 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/Synchronized/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/Synchronized/task.md: -------------------------------------------------------------------------------- 1 | `synchronized` keyword can protect critical section. -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - ReentrantLock 3 | - Synchronized 4 | -------------------------------------------------------------------------------- /Synchronization/Exclusive locks/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1102318636 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Synchronization/Semaphore/No concurrent jobs/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | //put your task here 3 | } -------------------------------------------------------------------------------- /Synchronization/Semaphore/No concurrent jobs/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: theory 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | -------------------------------------------------------------------------------- /Synchronization/Semaphore/No concurrent jobs/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 375809801 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Synchronization/Semaphore/No concurrent jobs/task.md: -------------------------------------------------------------------------------- 1 | You can use a semaphore to limit the number of concurrent tasks to *1*. 2 | In other words `Semaphore` can be used as an exclusive lock. -------------------------------------------------------------------------------- /Synchronization/Semaphore/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - No concurrent jobs 3 | -------------------------------------------------------------------------------- /Synchronization/Semaphore/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1787598917 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Synchronization/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Exclusive locks 3 | - CountDownLatch 4 | - Semaphore 5 | -------------------------------------------------------------------------------- /Synchronization/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1932336972 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with custom thread naming/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | 5 | ExecutorService create() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with custom thread naming/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with custom thread naming/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1006564911 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with custom thread naming/task.md: -------------------------------------------------------------------------------- 1 | Create a thread pool where threads are named `CustomPool-NUMBER`. 2 | Pool should have *at least 3 threads*. 3 | `NUMBER` should be a consecutive, unique number. 4 | This can be done by passing an instance of a `ThreadFactory` as a last argument to `ThreadPoolExecutor`'s constructor. 5 | Luckily there's a `com.google.common.util.concurrent.ThreadFactoryBuilder` in Guava that implements this interface. 6 | 7 |
8 | Check out `setNameFormat()` method with `%d` argument in `ThreadFactoryBuilder`. 9 |
10 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with custom thread naming/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ExecutorService; 4 | 5 | import org.assertj.core.api.WithAssertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests implements WithAssertions { 9 | 10 | public static final int THREAD_COUNT = 3; 11 | private final Events events = new Events<>(); 12 | 13 | @Test 14 | public void testThreadPrefix() { 15 | ExecutorService executorService = new Task().create(); 16 | String threadName = ConcurrentAssertions.waitFor(executorService.submit(() -> Thread.currentThread().getName())); 17 | assertThat(threadName) 18 | .startsWith("CustomPool-") 19 | .matches("CustomPool-\\d+"); 20 | } 21 | 22 | @Test 23 | public void testNamesAreUnique() { 24 | ExecutorService executorService = new Task().create(); 25 | CountDownLatch waitForAll = new CountDownLatch(3); 26 | for (int i = 0; i < THREAD_COUNT; i++) { 27 | executorService.submit(() -> { 28 | events.log(Thread.currentThread().getName()); 29 | waitForAll.countDown(); 30 | }); 31 | } 32 | ConcurrentAssertions.waitFor(waitForAll, "all tasks"); 33 | List threadNames = events.drainToList(); 34 | assertThat(threadNames) 35 | .hasSize(THREAD_COUNT) 36 | .doesNotHaveDuplicates(); 37 | } 38 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with limited queue/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | ExecutorService create() { 5 | throw new UnsupportedOperationException("TODO"); 6 | } 7 | 8 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with limited queue/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests4.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with limited queue/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 501803458 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with limited queue/task.md: -------------------------------------------------------------------------------- 1 | Create a thread pool with a fixed number of threads (**4**) and a queue limited to (**2**) tasks. 2 | 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool with limited queue/test/Tests4.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.ExecutorService; 3 | import java.util.concurrent.RejectedExecutionException; 4 | import java.util.stream.IntStream; 5 | 6 | import org.assertj.core.api.WithAssertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import static org.awaitility.Awaitility.await; 10 | 11 | public class Tests4 implements WithAssertions { 12 | 13 | private final Events events = new Events<>(); 14 | 15 | @Test 16 | public void poolAcceptsFourTasks() throws InterruptedException { 17 | ExecutorService executorService = new Task().create(); 18 | CountDownLatch firstStage = new CountDownLatch(4); 19 | CountDownLatch secondStage = new CountDownLatch(1); 20 | IntStream.range(0, 4).forEach(i -> 21 | executorService.submit((UncheckedRunnable) () -> { 22 | events.log("Task " + i + " started"); 23 | firstStage.countDown(); 24 | ConcurrentAssertions.waitFor(secondStage, "first 4 tasks to fire"); 25 | })); 26 | ConcurrentAssertions.waitFor(firstStage, "first 4 tasks started"); 27 | assertThat(events.drainToList()) 28 | .containsExactlyInAnyOrder("Task 0 started", "Task 1 started", "Task 2 started", "Task 3 started"); 29 | } 30 | 31 | @Test 32 | public void poolAcceptsTwoTasksInAQueue() throws InterruptedException { 33 | ExecutorService executorService = new Task().create(); 34 | CountDownLatch firstStage = new CountDownLatch(4); 35 | CountDownLatch secondStage = new CountDownLatch(1); 36 | IntStream.range(0, 6).forEach(i -> 37 | executorService.submit((UncheckedRunnable) () -> { 38 | events.log("Task " + i + " started"); 39 | firstStage.countDown(); 40 | ConcurrentAssertions.waitFor(secondStage, "first 4 tasks to fire"); 41 | })); 42 | ConcurrentAssertions.waitFor(firstStage, "first 4 tasks started"); 43 | assertThat(events.drainToList()) 44 | .containsExactlyInAnyOrder("Task 0 started", "Task 1 started", "Task 2 started", "Task 3 started"); 45 | secondStage.countDown(); 46 | await().untilAsserted(() -> assertThat(events.toList()).containsExactlyInAnyOrder("Task 4 started", "Task 5 started")); 47 | } 48 | 49 | @Test 50 | public void failsToAccept7thTask() throws InterruptedException { 51 | ExecutorService executorService = new Task().create(); 52 | CountDownLatch firstStage = new CountDownLatch(4); 53 | CountDownLatch secondStage = new CountDownLatch(1); 54 | IntStream.range(0, 6).forEach(i -> 55 | executorService.submit((UncheckedRunnable) () -> { 56 | events.log("Task " + i + " started"); 57 | firstStage.countDown(); 58 | ConcurrentAssertions.waitFor(secondStage, "first 4 tasks to fire"); 59 | })); 60 | ConcurrentAssertions.waitFor(firstStage, "first 4 tasks started"); 61 | try { 62 | executorService.submit(() -> { 63 | }); 64 | failBecauseExceptionWasNotThrown(RejectedExecutionException.class); 65 | } catch (RejectedExecutionException expected) { 66 | 67 | } 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | 5 | ExecutorService create() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests3.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1634397786 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool/task.md: -------------------------------------------------------------------------------- 1 | Create a thread pool with *4* worker threads. 2 | 3 |
4 | 5 | Use `Executors.newFixedThreadPool()`. 6 | 7 |
-------------------------------------------------------------------------------- /Thread pools/Creating/Fixed thread pool/test/Tests3.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.util.concurrent.Callable; 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.IntStream; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | public class Tests3 { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 16 | 17 | public static final int THREAD_COUNT = 4; 18 | 19 | @Test 20 | public void enoughThread() throws ExecutionException, InterruptedException { 21 | ExecutorService executor = new Task().create(); 22 | CountDownLatch wait = new CountDownLatch(THREAD_COUNT); 23 | CountDownLatch start = new CountDownLatch(1); 24 | IntStream.range(0, THREAD_COUNT) 25 | .mapToObj(x -> executor.submit((Callable) () -> { 26 | wait.countDown(); 27 | start.await(); 28 | return null; 29 | })).collect(Collectors.toList()); 30 | ConcurrentAssertions.waitFor(wait, "all tasks to start on " + THREAD_COUNT + " workers"); 31 | start.countDown(); 32 | } 33 | 34 | @Test 35 | public void tooManyThreads() throws ExecutionException, InterruptedException { 36 | //TODO How to test for too many worker thread in a thread pool? 37 | } 38 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread exeutor with limited queue/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | 5 | ExecutorService create() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread exeutor with limited queue/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread exeutor with limited queue/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1669280788 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread exeutor with limited queue/task.md: -------------------------------------------------------------------------------- 1 | Create an `ExecutorService` that uses just one worker thread. 2 | The queue in front of that executor should hold at most 10 tasks. 3 | 4 |
5 | Check out the implementation of `Executors.newSingleThreadExecutor()`, especially how the quue is defined. 6 |
7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread exeutor with limited queue/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | import java.util.concurrent.RejectedExecutionException; 3 | import java.util.concurrent.Semaphore; 4 | 5 | import org.assertj.core.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | public class Tests { 11 | 12 | @Test 13 | public void shouldAccept10tasksInQueue() { 14 | ExecutorService executor = new Task().create(); 15 | Semaphore semaphore = new Semaphore(1); 16 | for (int i = 0; i <= 10; i++) { 17 | executor.submit(() -> task(semaphore)); 18 | } 19 | 20 | assertThat(executor.shutdownNow().size()).isEqualTo(10); 21 | } 22 | 23 | @Test 24 | public void shouldReject11thTask() { 25 | ExecutorService executor = new Task().create(); 26 | Semaphore semaphore = new Semaphore(1); 27 | for (int i = 0; i <= 10; i++) { 28 | executor.submit(() -> task(semaphore)); 29 | } 30 | try { 31 | executor.submit(() -> task(semaphore)); 32 | Assertions.failBecauseExceptionWasNotThrown(RejectedExecutionException.class); 33 | } catch (RejectedExecutionException e) { 34 | //expected 35 | } 36 | 37 | assertThat(executor.shutdownNow().size()).isEqualTo(10); 38 | } 39 | 40 | private Object task(Semaphore semaphore) throws InterruptedException { 41 | if (!semaphore.tryAcquire()) { 42 | throw new IllegalStateException("Two tasks running at the same time?"); 43 | } 44 | try { 45 | Thread.sleep(1000); 46 | } finally { 47 | semaphore.release(); 48 | } 49 | return null; 50 | } 51 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread pool/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | 5 | ExecutorService create() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread pool/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread pool/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1020484716 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread pool/task.md: -------------------------------------------------------------------------------- 1 | Create an `Executor` that uses just one thread. 2 | 3 |
4 | Look at the `Executors` class for help. 5 |
6 | -------------------------------------------------------------------------------- /Thread pools/Creating/Single thread pool/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutionException; 2 | import java.util.concurrent.ExecutorService; 3 | import java.util.concurrent.Future; 4 | import java.util.concurrent.Semaphore; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests { 9 | 10 | @Test 11 | public void testSolution() throws ExecutionException, InterruptedException { 12 | ExecutorService executor = new Task().create(); 13 | Semaphore semaphore = new Semaphore(1); 14 | Future f1 = executor.submit(() -> task(semaphore)); 15 | Future f2 = executor.submit(() -> task(semaphore)); 16 | f1.get(); 17 | f2.get(); 18 | } 19 | 20 | private Object task(Semaphore semaphore) throws InterruptedException { 21 | if (!semaphore.tryAcquire()) { 22 | throw new IllegalStateException("Two tasks running at the same time?"); 23 | } 24 | try { 25 | Thread.sleep(1000); 26 | } finally { 27 | semaphore.release(); 28 | } 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /Thread pools/Creating/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Single thread pool 3 | - Fixed thread pool 4 | - Single thread exeutor with limited queue 5 | - Fixed thread pool with limited queue 6 | - Fixed thread pool with custom thread naming 7 | -------------------------------------------------------------------------------- /Thread pools/Creating/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 557558480 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Schedule task in the future/src/Task7.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ScheduledExecutorService; 2 | 3 | public class Task7 { 4 | 5 | void schedule(ScheduledExecutorService executorService, Runnable task) { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/Schedule task in the future/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task7.java 4 | visible: true 5 | - name: test/Tests7.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Schedule task in the future/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 23731526 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Schedule task in the future/task.md: -------------------------------------------------------------------------------- 1 | Schedule given task to run 3 seconds in the future. 2 | The task is not recurring, i.e. should run only once. -------------------------------------------------------------------------------- /Thread pools/Scheduling/Schedule task in the future/test/Tests7.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ScheduledThreadPoolExecutor; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class Tests7 { 10 | 11 | @Test 12 | public void testSolution() { 13 | ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1); 14 | CountDownLatch done = new CountDownLatch(1); 15 | new Task7().schedule(scheduledExecutorService, done::countDown); 16 | assertThat(scheduledExecutorService.getQueue()).hasSize(1); 17 | ConcurrentAssertions.waitFor(done, "scheduled task to be executed", Duration.ofSeconds(5)); 18 | assertThat(scheduledExecutorService.getQueue()).isEmpty(); 19 | } 20 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed delay/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ScheduledExecutorService; 2 | 3 | public class Task { 4 | void schedule(ScheduledExecutorService executorService, Runnable task) { 5 | throw new UnsupportedOperationException("TODO"); 6 | } 7 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed delay/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed delay/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 2105236113 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed delay/task.md: -------------------------------------------------------------------------------- 1 | Schedule a task to run with 3 seconds delay. 2 | This means that the second execution of the task happens exactly 3 seconds after the **completion** of the first execution. 3 | 4 | The initial delay should be 3 seconds as well. 5 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed delay/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ScheduledThreadPoolExecutor; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class Tests { 10 | 11 | @Test 12 | public void testSolution() { 13 | ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1); 14 | CountDownLatch done = new CountDownLatch(1); 15 | new Task().schedule(scheduledExecutorService, done::countDown); 16 | assertThat(scheduledExecutorService.getQueue()).hasSize(1); 17 | ConcurrentAssertions.waitFor(done, "scheduled task to be executed", Duration.ofSeconds(5)); 18 | assertThat(scheduledExecutorService.getQueue()).hasSize(1); 19 | } 20 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed interval/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ScheduledExecutorService; 2 | 3 | public class Task { 4 | void schedule(ScheduledExecutorService executorService, Runnable task) { 5 | throw new UnsupportedOperationException("TODO"); 6 | } 7 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed interval/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed interval/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1183941805 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed interval/task.md: -------------------------------------------------------------------------------- 1 | Schedule a task to run with 3 seconds delay. 2 | This means that the second execution of the task happens exactly 3 seconds after the **beginning** of the first execution. 3 | It also means if the task takes more than 3 seconds to run, subsequent executions will overlap. 4 | 5 | The initial delay should be 3 seconds as well. 6 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/Scheduling with fixed interval/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ScheduledThreadPoolExecutor; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class Tests { 10 | @Test 11 | public void testSolution() { 12 | ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1); 13 | CountDownLatch done = new CountDownLatch(1); 14 | new Task().schedule(scheduledExecutorService, done::countDown); 15 | assertThat(scheduledExecutorService.getQueue()).hasSize(1); 16 | ConcurrentAssertions.waitFor(done, "scheduled task to be executed", Duration.ofSeconds(5)); 17 | assertThat(scheduledExecutorService.getQueue()).hasSize(1); 18 | } 19 | } -------------------------------------------------------------------------------- /Thread pools/Scheduling/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Schedule task in the future 3 | - Scheduling with fixed delay 4 | - Scheduling with fixed interval 5 | -------------------------------------------------------------------------------- /Thread pools/Scheduling/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1162988208 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Thread pools/Shutdown/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/src/SystemOverflowException.java: -------------------------------------------------------------------------------- 1 | public class SystemOverflowException extends RuntimeException { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/src/Task.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ExecutorService; 2 | 3 | public class Task { 4 | 5 | ExecutorService create() { 6 | throw new UnsupportedOperationException("TODO"); 7 | } 8 | } -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | - name: src/SystemOverflowException.java 8 | visible: true 9 | -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 387291283 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/task.md: -------------------------------------------------------------------------------- 1 | Create a single-threaded thread pool with a queue of **5** tasks. 2 | When too many tasks are submitted, throw `SystemOverflowException`. 3 | 4 |
5 | You can pass custom `RejectedExecutionHandler` to the `ThreadPoolExecutor`'s constructor. 6 |
7 | -------------------------------------------------------------------------------- /Thread pools/Tuning/Handling rejection/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Callable; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ExecutorService; 4 | 5 | import org.assertj.core.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class Tests { 9 | 10 | @Test 11 | public void testSolution() { 12 | ExecutorService executorService = new Task().create(); 13 | CountDownLatch latch = new CountDownLatch(1); 14 | for (int i = 0; i < (5 + 1); i++) { 15 | executorService.submit((Callable) () -> { 16 | latch.await(); 17 | return null; 18 | }); 19 | } 20 | try { 21 | executorService.submit(() -> {}); 22 | Assertions.failBecauseExceptionWasNotThrown(SystemOverflowException.class); 23 | } catch (SystemOverflowException e) { 24 | //expected 25 | } 26 | latch.countDown(); 27 | } 28 | } -------------------------------------------------------------------------------- /Thread pools/Tuning/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Handling rejection 3 | -------------------------------------------------------------------------------- /Thread pools/Tuning/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1298781514 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Thread pools/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Creating 3 | - Tuning 4 | - Scheduling 5 | - Shutdown 6 | -------------------------------------------------------------------------------- /Thread pools/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1798364808 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Creating/Creating plain threads/src/Task.java: -------------------------------------------------------------------------------- 1 | public abstract class Task { 2 | 3 | Thread createThread() { 4 | throw new UnsupportedOperationException("TODO"); 5 | } 6 | 7 | abstract void runThis(); 8 | 9 | } -------------------------------------------------------------------------------- /Threads/Creating/Creating plain threads/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests2.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Threads/Creating/Creating plain threads/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1581885825 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Creating/Creating plain threads/task.md: -------------------------------------------------------------------------------- 1 | Create a thread that runs `runThis()` method. 2 | Do not start this thread. 3 | 4 |
5 | You can either extend `Thread` class and override `run()` method, or create a new class that extends `Thread` and implements `runThis()` method. 6 | Pass `runThis()` method to the thread's constructor. 7 |
8 | -------------------------------------------------------------------------------- /Threads/Creating/Creating plain threads/test/Tests2.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.atomic.AtomicReference; 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class Tests2 { 9 | @Test 10 | public void testSolution() throws InterruptedException { 11 | AtomicReference threadName = new AtomicReference<>(); 12 | CountDownLatch latch = new CountDownLatch(1); 13 | Thread thread = new Task() { 14 | @Override 15 | void runThis() { 16 | threadName.set(Thread.currentThread().getName()); 17 | latch.countDown(); 18 | } 19 | }.createThread(); 20 | 21 | assertThat(threadName).hasValue(null); 22 | assertThat(thread).isNotNull(); 23 | thread.start(); 24 | ConcurrentAssertions.waitFor(latch, "condition"); 25 | assertThat(threadName.get()).startsWith("Thread-"); 26 | } 27 | } -------------------------------------------------------------------------------- /Threads/Creating/Do not extend Thread/src/Task.java: -------------------------------------------------------------------------------- 1 | public abstract class Task { 2 | 3 | Thread createThread() { 4 | throw new UnsupportedOperationException("TODO"); 5 | } 6 | 7 | abstract void runThis(); 8 | 9 | } -------------------------------------------------------------------------------- /Threads/Creating/Do not extend Thread/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Threads/Creating/Do not extend Thread/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 489175349 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Creating/Do not extend Thread/task.md: -------------------------------------------------------------------------------- 1 | Create a `Thread` that runs `runThis()` without extending the `Thread` class. 2 | Do **not** start that thread. 3 | 4 |
5 | You can pass `Runnable` to a `Thread`s constructor. 6 |
7 | -------------------------------------------------------------------------------- /Threads/Creating/Do not extend Thread/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | public class Tests { 8 | 9 | @Test 10 | public void testSolution() throws InterruptedException { 11 | CountDownLatch latch = new CountDownLatch(1); 12 | Thread thread = new Task() { 13 | @Override 14 | void runThis() { 15 | latch.countDown(); 16 | } 17 | }.createThread(); 18 | assertThat(thread).isNotNull(); 19 | assertThat(thread).isOfAnyClassIn(Thread.class); 20 | thread.start(); 21 | ConcurrentAssertions.waitFor(latch, "condition"); 22 | } 23 | } -------------------------------------------------------------------------------- /Threads/Creating/Start plain threads/src/Task.java: -------------------------------------------------------------------------------- 1 | public abstract class Task { 2 | 3 | void startThread() { 4 | throw new UnsupportedOperationException("TODO"); 5 | } 6 | 7 | abstract void runThis(); 8 | 9 | } -------------------------------------------------------------------------------- /Threads/Creating/Start plain threads/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Threads/Creating/Start plain threads/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1362737172 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Creating/Start plain threads/task.md: -------------------------------------------------------------------------------- 1 | Create a thread that runs `runThis()` method. 2 | Start that thread immediately. -------------------------------------------------------------------------------- /Threads/Creating/Start plain threads/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.atomic.AtomicReference; 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class Tests { 9 | @Test 10 | public void testSolution() throws InterruptedException { 11 | AtomicReference threadName = new AtomicReference<>(); 12 | CountDownLatch latch = new CountDownLatch(1); 13 | Task task = new Task() { 14 | @Override 15 | void runThis() { 16 | threadName.set(Thread.currentThread().getName()); 17 | latch.countDown(); 18 | } 19 | }; 20 | assertThat(threadName).hasValue(null); 21 | task.startThread(); 22 | ConcurrentAssertions.waitFor(latch, "condition"); 23 | assertThat(threadName.get()).startsWith("Thread-"); 24 | } 25 | } -------------------------------------------------------------------------------- /Threads/Creating/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Creating plain threads 3 | - Start plain threads 4 | - Do not extend Thread 5 | -------------------------------------------------------------------------------- /Threads/Creating/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 453943280 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Threads/Managing/Naming threads/src/Task.java: -------------------------------------------------------------------------------- 1 | public abstract class Task { 2 | 3 | Thread createThread() { 4 | throw new UnsupportedOperationException("TODO"); 5 | } 6 | 7 | abstract void runThis(); 8 | 9 | } -------------------------------------------------------------------------------- /Threads/Managing/Naming threads/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Threads/Managing/Naming threads/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 144189617 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Managing/Naming threads/task.md: -------------------------------------------------------------------------------- 1 | Create a thread named `"Course"` that runs `runThis()` method. 2 | Do not start this thread. -------------------------------------------------------------------------------- /Threads/Managing/Naming threads/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.atomic.AtomicReference; 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class Tests { 9 | @Test 10 | public void testSolution() throws InterruptedException { 11 | AtomicReference threadName = new AtomicReference<>(); 12 | CountDownLatch latch = new CountDownLatch(1); 13 | Thread thread = new Task() { 14 | @Override 15 | void runThis() { 16 | threadName.set(Thread.currentThread().getName()); 17 | latch.countDown(); 18 | } 19 | }.createThread(); 20 | 21 | assertThat(threadName).hasValue(null); 22 | assertThat(thread).isNotNull(); 23 | thread.start(); 24 | ConcurrentAssertions.waitFor(latch, "condition"); 25 | assertThat(threadName).hasValue("Course"); 26 | } 27 | } -------------------------------------------------------------------------------- /Threads/Managing/Waiting for thread to complete/src/Task.java: -------------------------------------------------------------------------------- 1 | public class Task { 2 | void waitFor(Thread thread) throws InterruptedException { 3 | throw new UnsupportedOperationException("TODO"); 4 | } 5 | } -------------------------------------------------------------------------------- /Threads/Managing/Waiting for thread to complete/task-info.yaml: -------------------------------------------------------------------------------- 1 | type: edu 2 | files: 3 | - name: src/Task.java 4 | visible: true 5 | - name: test/Tests.java 6 | visible: true 7 | -------------------------------------------------------------------------------- /Threads/Managing/Waiting for thread to complete/task-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 352176302 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /Threads/Managing/Waiting for thread to complete/task.md: -------------------------------------------------------------------------------- 1 | Wait until given running `thread` instance completes execution. 2 | Use `Thread.join()`. 3 | 4 | Can a running thread return some computed value? -------------------------------------------------------------------------------- /Threads/Managing/Waiting for thread to complete/test/Tests.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CountDownLatch; 2 | import java.util.concurrent.TimeUnit; 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat; 7 | 8 | public class Tests { 9 | @Test 10 | public void testSolution() throws InterruptedException { 11 | CountDownLatch latch = new CountDownLatch(1); 12 | Thread thread = new Thread(() -> { 13 | try { 14 | TimeUnit.SECONDS.sleep(1); 15 | } catch (InterruptedException e) { 16 | e.printStackTrace(); 17 | } 18 | latch.countDown(); 19 | }); 20 | thread.start(); 21 | new Task().waitFor(thread); 22 | assertThat(latch.await(1, TimeUnit.MILLISECONDS)).isTrue(); 23 | } 24 | } -------------------------------------------------------------------------------- /Threads/Managing/lesson-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Naming threads 3 | - Waiting for thread to complete 4 | -------------------------------------------------------------------------------- /Threads/Managing/lesson-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 1212197525 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | unit: 0 4 | -------------------------------------------------------------------------------- /Threads/section-info.yaml: -------------------------------------------------------------------------------- 1 | content: 2 | - Creating 3 | - Managing 4 | -------------------------------------------------------------------------------- /Threads/section-remote-info.yaml: -------------------------------------------------------------------------------- 1 | id: 726443728 2 | update_date: Thu, 01 Jan 1970 00:00:00 UTC 3 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | } 6 | 7 | def printOutput(def output) { 8 | return tasks.create("printOutput") { 9 | println "#educational_plugin_checker_version 1" 10 | def separator = System.lineSeparator() 11 | def lines = output.toString().split("(?<=${separator})|(?=${separator})") 12 | for (line in lines) { 13 | println "#educational_plugin" + line 14 | } 15 | } 16 | } 17 | 18 | subprojects { 19 | apply plugin: 'application' 20 | apply plugin: 'java' 21 | 22 | sourceCompatibility = 11 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | dependencies { 29 | implementation 'org.junit.jupiter:junit-jupiter:5.8.2' 30 | implementation 'org.assertj:assertj-core:3.22.0' 31 | implementation 'ch.qos.logback:logback-classic:1.2.11' 32 | implementation 'com.google.guava:guava:31.1-jre' 33 | implementation 'org.mockito:mockito-core:4.4.0' 34 | implementation 'org.awaitility:awaitility:4.2.0' 35 | implementation 'io.micrometer:micrometer-registry-graphite:1.8.4' 36 | 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | testLogging { 42 | showStandardStreams = true 43 | } 44 | } 45 | 46 | sourceSets { 47 | main { 48 | java.srcDir 'src' 49 | } 50 | test { 51 | java.srcDir 'test' 52 | } 53 | } 54 | 55 | mainClassName = project.hasProperty("mainClass") ? project.getProperty("mainClass") : "" 56 | 57 | if (project.hasProperty("educationalRun") && "true".equalsIgnoreCase(project.getProperty("educationalRun"))) { 58 | def runOutput = new ByteArrayOutputStream() 59 | tasks.run.setStandardOutput(runOutput) 60 | tasks.run.doLast { printOutput(runOutput) } 61 | } 62 | } 63 | 64 | project(':util') { 65 | dependencies { 66 | } 67 | } 68 | 69 | configure(subprojects.findAll { it.name != 'util' }) { 70 | dependencies { 71 | implementation project(':util').sourceSets.main.output 72 | implementation project(':util').sourceSets.test.output 73 | } 74 | } -------------------------------------------------------------------------------- /course-info.yaml: -------------------------------------------------------------------------------- 1 | title: Java Core Concurrency 2 | language: English 3 | summary: | 4 | * Threads 5 | * Thread pools 6 | * Cancellation 7 | * CompletableFuture 8 | * Data structures 9 | * Monitoring 10 | * Synchronization 11 | * Java Memory Model 12 | vendor: 13 | name: Tomasz Nurkiewicz 14 | url: https://nurkiewicz.com 15 | programming_language: Java 16 | programming_language_version: 11 17 | type: edu 18 | content: 19 | - Introduction 20 | - Threads 21 | - Thread pools 22 | - Cancellation 23 | - CompletableFuture 24 | - Data structures 25 | - Monitoring 26 | - Synchronization 27 | - Java Memory Model 28 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurkiewicz/java-concurrency-workshop/65ab5d97bdabc00bdb919d60f74b0b1d872afdcd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | static String sanitizeName(String name) { 2 | return name.replaceAll("[ /\\\\:<>\"?*|()]", "_").replaceAll("(^[.]+)|([.]+\$)", "") 3 | } 4 | 5 | rootProject.name = 'java-concurrency' 6 | 7 | rootProject.projectDir.eachDirRecurse { 8 | if (!isTaskDir(it) || it.path.contains(".idea")) { 9 | return 10 | } 11 | def taskRelativePath = rootDir.toPath().relativize(it.toPath()) 12 | def parts = [] 13 | for (name in taskRelativePath) { 14 | parts.add(sanitizeName(name.toString())) 15 | } 16 | def moduleName = parts.join("-") 17 | include "$moduleName" 18 | project(":$moduleName").projectDir = it 19 | } 20 | 21 | def isTaskDir(File dir) { 22 | return new File(dir, "src").exists() 23 | } 24 | 25 | include 'util' -------------------------------------------------------------------------------- /util/src/test/java/ConcurrentAssertions.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.CountDownLatch; 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.Future; 5 | import java.util.concurrent.Semaphore; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.TimeoutException; 8 | 9 | import com.google.common.base.Throwables; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | public class ConcurrentAssertions { 14 | 15 | public static void waitFor(CountDownLatch latch, String waitingForWhat) { 16 | waitFor(latch, waitingForWhat, Duration.ofSeconds(3)); 17 | } 18 | 19 | public static void waitFor(CountDownLatch latch, String waitingForWhat, Duration timeout) { 20 | try { 21 | assertThat(latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) 22 | .describedAs("Waiting for " + waitingForWhat) 23 | .isTrue(); 24 | } catch (InterruptedException e) { 25 | throw new RuntimeException("Waiting for " + waitingForWhat + " was interrupted", e); 26 | } 27 | } 28 | 29 | public static void tryAcquire(Semaphore semaphore, String tooManyOfWhat) { 30 | assertThat(semaphore.tryAcquire()) 31 | .describedAs("Too many " + tooManyOfWhat) 32 | .isTrue(); 33 | } 34 | 35 | public static T waitFor(Future future) { 36 | try { 37 | return future.get(5, TimeUnit.SECONDS); 38 | } catch (ExecutionException e) { 39 | Throwables.throwIfUnchecked(e.getCause()); 40 | throw new RuntimeException(e); 41 | } catch (TimeoutException e) { 42 | throw new RuntimeException("Waited too long", e); 43 | } catch (InterruptedException e) { 44 | throw new RuntimeException("Task was interrupted", e); 45 | } 46 | } 47 | 48 | public static void waitForFutures(Iterable> futures) throws InterruptedException { 49 | futures.forEach(ConcurrentAssertions::waitFor); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /util/src/test/java/ErrorCollectingRunnable.java: -------------------------------------------------------------------------------- 1 | class ErrorCollecting { 2 | 3 | public static Runnable collectErrors(Events errors, Runnable runnable) { 4 | return () -> { 5 | try { 6 | runnable.run(); 7 | } catch (Exception e) { 8 | errors.log(e); 9 | throw e; 10 | } 11 | }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /util/src/test/java/Events.java: -------------------------------------------------------------------------------- 1 | import java.lang.invoke.MethodHandles; 2 | import java.util.ArrayList; 3 | import java.util.List; 4 | import java.util.concurrent.ArrayBlockingQueue; 5 | import java.util.concurrent.BlockingQueue; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class Events { 11 | 12 | private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 13 | 14 | private final BlockingQueue queue = new ArrayBlockingQueue<>(1_000_000); 15 | 16 | void log(T event) { 17 | log.info("Event {}", event); 18 | queue.add(event); 19 | } 20 | 21 | List drainToList() { 22 | List list = new ArrayList<>(); 23 | queue.drainTo(list); 24 | return list; 25 | } 26 | 27 | ArrayList toList() { 28 | return new ArrayList<>(queue); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /util/src/test/java/FutureFailingWhenBlocked.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.CompletableFuture; 2 | import java.util.concurrent.ExecutionException; 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | class FutureFailingWhenBlocked extends CompletableFuture { 7 | 8 | public FutureFailingWhenBlocked() { 9 | } 10 | 11 | public FutureFailingWhenBlocked(T value) { 12 | this.complete(value); 13 | } 14 | 15 | @Override 16 | public T join() { 17 | throw new UnsupportedOperationException("Blocking is not supported!"); 18 | } 19 | 20 | @Override 21 | public T get() throws InterruptedException, ExecutionException { 22 | return join(); 23 | } 24 | 25 | @Override 26 | public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 27 | return join(); 28 | } 29 | 30 | @Override 31 | public T getNow(T valueIfAbsent) { 32 | return join(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /util/src/test/java/Sleeper.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.concurrent.ThreadLocalRandom; 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class Sleeper { 6 | 7 | public static void sleepRandomly(Duration duration) { 8 | long millis = duration.toMillis(); 9 | double randMillis = millis + ThreadLocalRandom.current().nextGaussian() * millis / 10; 10 | sleep(Duration.ofMillis((long) randMillis)); 11 | } 12 | 13 | public static void sleep(Duration duration) { 14 | try { 15 | TimeUnit.MILLISECONDS.sleep(duration.toMillis()); 16 | } catch (InterruptedException ignored) { 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /util/src/test/java/UncheckedRunnable.java: -------------------------------------------------------------------------------- 1 | import com.google.common.base.Throwables; 2 | 3 | @FunctionalInterface 4 | public interface UncheckedRunnable extends Runnable { 5 | 6 | default void run() { 7 | try { 8 | uncheckedRun(); 9 | } catch (Exception e) { 10 | Throwables.throwIfUnchecked(e); 11 | throw new RuntimeException(e); 12 | } 13 | } 14 | 15 | void uncheckedRun() throws Exception; 16 | } 17 | --------------------------------------------------------------------------------