{"id":20464441,"url":"https://github.com/emwork/java-concurrency","last_synced_at":"2025-09-11T01:40:00.695Z","repository":{"id":214119675,"uuid":"582681024","full_name":"emwork/java-concurrency","owner":"emwork","description":"Java concurrency and async examples","archived":false,"fork":false,"pushed_at":"2023-12-26T01:23:13.000Z","size":26,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-16T01:14:43.881Z","etag":null,"topics":["async","concurrency","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-27T15:06:30.000Z","updated_at":"2022-12-27T16:30:29.000Z","dependencies_parsed_at":"2024-02-01T20:06:11.728Z","dependency_job_id":null,"html_url":"https://github.com/emwork/java-concurrency","commit_stats":null,"previous_names":["emwork/java-concurrency"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emwork%2Fjava-concurrency","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emwork%2Fjava-concurrency/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emwork%2Fjava-concurrency/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emwork%2Fjava-concurrency/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emwork","download_url":"https://codeload.github.com/emwork/java-concurrency/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242024177,"owners_count":20059484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","concurrency","java"],"created_at":"2024-11-15T13:15:00.935Z","updated_at":"2025-03-05T12:21:31.859Z","avatar_url":"https://github.com/emwork.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Concurrent collections\n\nWhen considering concurrent collections and maps it's important to know your workload.\nWhile there's no one solution that solves all the problems, and there are pros and cons in every approach.\nFor example, the number of threads could be in low numbers - 3, 5, 10, 20 - as opposed to running tens of thousands threads, and obviously approach cannot be the same.\nOr you may have a large vs low count of the concurrently worked on objects.\nIf you use synchronization and you have a low count of objects you will be blocking too many of your threads.\nIf you use copy-on-write and you have a spike in writes, you will have high memory consumption, high CPU and high GC activity.\n\n[See examples: Concurrency basics](#concurrency-basics)\n\n[See examples: Java concurrent collections implementation](#java-concurrent-collections-implementation)\n\n\n### Copy-on-write\n\nCopy-on-write will not use locking at all for reads, and there are structures for lists and sets.\nThe writes will simply create new objects and synchronously update references to the new objects from the old ones (the threads that are actively working with the old objects will not see the change though, only the new reads will see the new objects)\nThis works extremely well for the applications that have very few writes, but a ton of reads - for example reading app config that rarely changes.\nJava classes that implement copy-on-write:\n\tCopyOnWriteArrayList\u003c?\u003e\n\tCopyOnWriteArraySet\u003c?\u003e\n\n[See examples: Java concurrent collections implementation](#java-concurrent-collections-implementation)\n\n### Queues and double-ended queues\nIn concurrency situations you cold use Queue, Deque, ArrayBlockingQueue, ConcurrentLinkedQueue - depending on your application load.\n\n- ArrayBlockingQueue (FIFO) is implemented in a way that it's not extendable, since it's backed by a fixed size array\n- ConcurrentLinkedQueue (FIFO) is on the other hand extendable/unbounded\n- Queue can be seen as a FIFO structure\n- Deque is both FIFO and LIFO (or a stack), and it should be noted that there's no \"pure\" stack implementation in Java. (java.util.Stack is old and inefficient)\n\nWhen considering how to handle a full blocking queue recall that there are these methods:\n- add (will throw IllegalStateException if the queue is full)\n- put (will block, waiting for space to become available if the queue is full) \n- offer (will return false if the capacity had been reached) \n- offer with timeout (will return false after timeout, if the capacity had been reached)\t\n\nWhen the blocking queue is empty and you are trying to get an element - these are approaches you can take to handle this situation:\n- poll() will return null if the queue is empty\n- peek() will return null if the queue is empty\n- remove() will throw NoSuchElementException - if this queue is empty\n- element() will throw NoSuchElementException - if this queue is empty\n- take() will block if the queue is empty\n\nDeque is implemented by ConcurrentLinkedDeque and LinkedBlockingDeque\n - these have methods to add/remove elements from the hear or tail\n\n[See examples: Java concurrent collections implementation](#java-concurrent-collections-implementation)\n \n\n### Concurrent Map, Set\nAll ConcurrentMap operations are thread-safe, retrieval operations do not entail locking, and there isn't any support for locking the entire table in a way that prevents all access\n\nGet can overlap with put and remove, no blocking will occur. You will simply get the result of the most recently computed operation (put/delete)\n\nConcurrentMap operations are atomic, e.g.\n - putIfAbsent(arg0, arg1);\n - remove(arg0), (same as removeIfPresent)\n - replace(k, v)\n - replace(k, oldV, newV)\n\t\t\nAlso: ConcurrentHashMaps support a set of sequential and parallel bulk operations that are designed to be safely, and often sensibly, applied even with maps that are being concurrently updated by other threads. \n\nParallel methods are:\n - search(parallelismThreshold, searchFunction)\n - reduce(parallelismThreshold, transformer, reducer)\n - forEach(parallelismThreshold, biConsumer);\n \n\nConcurrent hash sets are implemented via ConcurrentHashMap.newKeySet()\n\nConcurrentSkipListMap is another implementation of ConcurrentMap. It's a fully concurrent map, with its keys sorted, based on a SkipList structure (used to create LinkedList).  \nInsertion, removal, update, and access operations safely execute concurrently by multiple threads.  \nThis implementation doesn't reply on any synchronization, it uses AtomicReference operations.  \nConcurrentSkipListMap is useful when iteration order is important, since ConcurrentHashMaps doesn't guarantee the order.\n\nThere's also a ConcurrentSkipListSet, based on a SkipList structure. Behavior and implementation is similar to ConcurrentSkipListMap.\n\n[See examples: Java concurrent collections implementation](#java-concurrent-collections-implementation)\n\n### CyclicBarrier\nCyclicBarrier is a concurrency structure that allows multiple threads to run until a certain point (a barrier) and then wait for each other until the whole group has reached the barrier before continuing.\nThe call to the await() method is that dividing point - and it allows threads to either wait indefinitely or until a timeout has been reached.\nThe barrier can throw a TimeoutException and enter a \"broken\" state, notifying other parties of the state change.\nIt's possible to have a callback executed once the barrier opens, executing a specific task before the \"after-the-barrier-point\" code continues.\nThe barrier is called cyclic because it can be re-used after the threads waiting for it to open are released. The barrier opens to release the threads and then closes.\n\n[See examples: Locks, Semaphores, Barriers and Producer-Consumer implementation](#locks-semaphores-barriers-and-producer-consumer-implementation)\n\n### Semaphore \nSemaphore is a concurrency structure similar to the CyclicBarrier, but much simpler.\nIt allows creating a limited number of the pass-through permits, these can be concurrently acquired, and - after doing some work - released for other threads to be acquired in turn. These permits can be used to limit access to a concurrently used resource.\n\n[See examples: Locks, Semaphores, Barriers and Producer-Consumer implementation](#locks-semaphores-barriers-and-producer-consumer-implementation)\n\n\n### Concurrency alternative: Async solutions with CompletableFuture, CompletionStage\n\nCompletableFuture adds the following benefits to the regular Future construct:\n- Exception handling\n- Chaining and combining futures together in pipeline-like flows (performing async computations that depend on the other async computations)\n- Attaching a callback that gets executed when the future is completed\n- java.util.concurrent.CompletionStage is implemented by CompletableFuture and allows performing further actions after other(s) CompletionStage/CompletableFuture completes\n\n\nFor example, to attach a callback to the future we would use CompletableFuture.thenAccept(Consumer\u003c? super T\u003e) if your Supplier that was passed to supplyAsync() returns T.\n\nYou can also chain multiple callbacks, using thenApply like so:\nCompletableFuture.supplyAsync(this::mySupplyFunction)\n        \t\t.thenApply(s  -\u003e {log.info(\"Step 2: gets mySupplyFunction result {}\", s); return 5; })\n        \t\t.thenAccept(s -\u003e  log.info(\"Step 3: gets step's two result (integer 5): {}\", s));\n        \t\t\nThe CompletableFuture's methods that end in \"Async\" (e.g. thenApplyAsync) will run each stage on a different thread, whereas thenApply, thenAccept etc. will continue on the thread where supplyAsync started.\n\n[See examples: Async execution with CompletableFuture](#async-execution-with-completablefuture)\n\n\n-----------------------------------------------\n\n\n## Explanation of the code examples\n\n### Concurrency basics\n1. ca.skylinedata.javatips.concurrency.basics.**Callables** - this example covers Java Callable, ExecutorService, Future\n1. ca.skylinedata.javatips.concurrency.basics.**Runnables** - this example covers Java Runnable, ExecutorService, Future, CountDownLatch\n1. ca.skylinedata.javatips.concurrency.basics.**ExecutorsServiceDemo** - this example covers Java Runnable, ExecutorService\n1. ca.skylinedata.javatips.concurrency.basics.**Casing** - this example covers Java Atomic classes that implement Compare-And-Swap design patterns\n\n\n### Locks, Semaphores, Barriers and Producer-Consumer implementation\n1. ca.skylinedata.javatips.concurrency.locks.**ReentrantLockDemo** - this example covers ReentrantLock, ExecutorService\n1. ca.skylinedata.javatips.concurrency.locks.**CacheWithReadWriteLock** - this example covers ReadWriteLock, ExecutorService\n1. ca.skylinedata.javatips.concurrency.locks.**CyclicBarrierTasks** - this example covers CyclicBarrier, ExecutorService\n1. ca.skylinedata.javatips.concurrency.locks.**CyclicBarrierExceptionHandling** - this example covers CyclicBarrier, ExecutorService, Runnable, Future, BrokenBarrierExceptions\n1. ca.skylinedata.javatips.concurrency.locks.**SemaphoreTasks** - this example covers Semaphore, ExecutorService\n1. ca.skylinedata.javatips.concurrency.locks.**ProducerConsumerWithLocks** - this example covers Locks, Conditions, Callable, ExecutorService\n\n\n### Java concurrent collections implementation\n1. ca.skylinedata.javatips.concurrency.collections.**ConcurrentMapExample** - this example covers ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet\n1. ca.skylinedata.javatips.concurrency.collections.**QueueDequeExample** - this example covers ArrayBlockingQueue, ConcurrentLinkedQueue, Deque\n1. ca.skylinedata.javatips.concurrency.collections.**CopyOnWriteExample** - this example covers CopyOnWriteArrayList used in in high concurrency situations\n1. ca.skylinedata.javatips.concurrency.collections.**ConsumerProducerWithBlockingQueue** - this example covers ArrayBlockingQueue, Callable. ExecutorService\n\n### Async execution with CompletableFuture\n1. ca.skylinedata.javatips.async.**CompletableFutureExample** is a demo that uses CompletableFuture to define multi-threaded order processing pipeline\n1. ca.skylinedata.javatips.async.**CompletingFutureExample** is a CompletableFuture example how we can force-complete a future if we decide so. Something we couldn't do with a regular Future\n\n\n## How to run\n- You can checkout the project with git clone command and open it in your favourite IDE, then run each demo as necessary, adjusting code and seeing the results\n- Or you can run from the command line using maven exec plugin, like so:\n\n```\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.basics.Callables\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.basics.Runnables\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.basics.ExecutorsServiceDemo\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.basics.Casing\"\n\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.ReentrantLockDemo\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.CacheWithReadWriteLock\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.CyclicBarrierTasks\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.CyclicBarrierExceptionHandling\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.SemaphoreTasks\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.locks.ProducerConsumerWithLocks\"\n\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.collections.ConcurrentMapExample\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.collections.QueueDequeExample\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.collections.CopyOnWriteExample\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.concurrency.collections.ConsumerProducerWithBlockingQueue\"\n\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.async.CompletableFutureExample\"\nmvn exec:java -Dexec.mainClass=\"ca.skylinedata.javatips.async.CompletingFutureExample\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femwork%2Fjava-concurrency","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femwork%2Fjava-concurrency","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femwork%2Fjava-concurrency/lists"}