Basic building blocks in Java
The Java platform class library contains rich concurrency foundation building blocks, such as thread-safe container classes and various synchronization utility classes for coordinating multiple, cooperating thread control flows.
1. Synchronize container classes
Synchronous container classes are thread-safe, but in some cases additional client-side locking may be required to protect composite operations. Common compound operations include iteration, jumps (between elements in a container), and conditional operations (such as “add if not”).
Implicit iteration: In some cases iteration operations are hidden. Println calls the toString method of a Set and then iterates over the Set to call the toString method:
public class HiddenIterator { private final Set<Integer> set = new HashSet<Integer>(); public void addTenThings() { Random r = new Random(); for (int i = 0; i < 10; i++) add(r.nextInt()); System.out.println("Debug : " + set); }}Copy the code
2. Concurrent container classes
Concurrent containers are designed specifically for concurrent access by multiple threads. By using concurrent containers instead of synchronous containers, you can greatly improve scalability and reduce risk. 1. ConcurrentHashMap: a hashed Map that replaces synchronization. The interface supports common operations, such as “If not, add”, replace, and conditional delete. ConcurrentHashMap uses a finer-grained locking mechanism: segmented locking to achieve a greater degree of sharing and improve throughput in concurrent environments. ConcurrentHashMap cannot be locked to perform exclusive access. In practice, use of ConcurrentHashMap should be abandoned only when the application needs to lock for exclusive access.
Atomic operations in ConcurrentHashMap:
methods | instructions |
---|---|
v putIfAbsent(K key,V value) | Insert only if k has no corresponding mapping value |
boolean remove(K key,V value) | It is removed only if k is mapped to v |
boolean replace(K key,V oldValue,V newValue) | Replace only if k is mapped to oldValue |
V replace(K key,V newValue) | Substitutions are made only if k is mapped to a value |
-
CopyOnWriteArrayLIst: Replaces synchronous List, provides better concurrency performance, and does not require container locking or copying during iteration. The thread-safety of copy-on-write containers is that as long as a de facto immutable object is properly published, no further control is required to access that object, and a new Copy of the container is created and published with each change, thereby achieving variability. The copy-on-write container should be used only when iteration is much more than modification.
-
BlockingQueue (producer —- consumer) : Adds blocking operations such as insert and fetch to Queue. If the queue is empty, fetching an element will block until there is one available in the queue, and if the queue is full (for bounded queues), inserting will block until there is space available in the queue. Blocking queues provide blocking take and PUT methods, as well as offer and poll methods that support timing. Bounded queues are a powerful resource management tool when building highly reliable applications: they suppress and generate excessive work items, making applications more robust when overloaded. Therefore, resource management mechanisms should be built into the design through blocking queues.
- LinkedBlockingQueue
- ArrayBlockingQueue
- PriorityBlockingQueue
Copy the code
Serial thread closure: A thread-closed object can only be owned by one thread, but ownership can be “transferred” by safely publishing the object. Once ownership is transferred, only another thread can gain access to the object, and the thread that published the object can no longer access it.
- Deque—->BlockingDeque—->ArrayBlockingQueue, LinkedBlockingQueue
3. Blocking and interruption
Threads block because: – Waiting for I/O operation to end – waiting to acquire a lock – waiting to wake up from sleep – waiting for calculation results from another thread
Thread.interrupt() is used to interrupt a Thread. The thread.interrupt () method does not interrupt a running Thread. What this method actually does is throw an interrupt signal when the thread is blocked so that it can exit the blocked state. More specifically, if a Thread is blocked by one of object. wait, Thread.join, or Thread.sleep, it receives an InterruptedException that terminates the blocked state prematurely.
Therefore, if a thread is blocked by any of these methods, the correct way to stop the thread is to set the shared variable and call interrupt () (note that the variable should be set first). If the thread is not blocked, calling interrupt () will not work; Otherwise, the thread gets an exception (which it must be prepared to handle) and escapes the blocking state. In either case, eventually the thread will check the shared variable and stop.
4. Synchronize tool classes
The synchronization utility class can be any object, as long as it coordinates the control flow of the thread according to its own state. Blocking queues can be used as synchronization utility classes. Other types of synchronization utility classes include Semaphore, Barrier, and Latch.
- Latches: LATches are a synchronization utility class that delays the progress of a thread until it reaches a terminated state. A latch acts like a door: the door remains closed and no thread can pass through until the latch reaches the end state, when the door opens to allow all threads to pass through. When the lock reaches the end state, it will never change state, so the door will remain open forever.Latches can be used to ensure that some activities do not continue until all others have completed, such as:
- Ensure that a computation does not proceed until all the resources it needs have been initialized
- Ensure that a service is started only after all other services it depends on have been started
- Wait until all participants of an action (such as all players in a multi-player game) are ready before proceeding.
Java provides an implementation of locking: CountDownLatch. It can cause one or more threads to wait for a set of times to occur. The locked state consists of a counter that is initialized to a positive number representing the number of events that need to wait. The countDown() method decrement the counter, indicating that a time has occurred; The await() method waits for the counter to reach 0, indicating that all events to wait have occurred. If the counter is non-0, await blocks until the counter is 0, or the waiting thread interrupts or times out.
-
FutureTask FutureTask implements the Future semantics and represents an abstract computation-yielding result. The behavior of future. get depends on the state of the task. If the task is completed, get will return the result immediately, otherwise GET will block until the task is completed, then return the result or throw an exception. The calculation represented by FutureTask is implemented through a Callable, which is a Runnable that produces results and can be in three states: Waiting to run, Running, or Completed. For more information on FutureTask, see Blog: Java’s method for creating threads
-
Counting Semaphore is used to control the number of operations that access a specific resource or execute a specific operation simultaneously. Counting Semaphore can also be used to implement a resource pool or impose boundaries on containers.
-
A fence a fence is similar to a lock in that it blocks a group of threads until an event occurs. The key difference between a fence and a lock is that all threads must reach the fence at the same time before execution can continue. A lock is used to wait for events, and a fence is used to wait for other threads. If all threads reach the fence position, the fence opens, all threads are released, and the fence is reset for next use. Cyclicbarriers are provided in Java to implement fences.