7. Callable interface
1. Multiple ways to create threads
Creation Method | Remarks |
---|---|
Integrate Thread Class | No return value |
Implement Runnable Interface | No return value |
Through the Callable interface | Can have a return value |
Through thread pools |
2. Differences between Callable and Runnable interfaces
Difference | Callable | Runnable |
---|---|---|
Is there a return value | yes | no |
Whether to throw an exception | yes | no |
Method implementations are different | call method | run method |
Code implementation:
package com.codetip.codejuc.juc.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; // Implement Runnable Interface class Mythread1 implements Runnable { @Override public void run() { } } // Implement Callable Interface class Mythread2 implements Callable<Integer> { @Override public Integer call() throws Exception { return 200; } } public class CallOrRunDemo1 { public static void main(String[] args) throws InterruptedException, ExecutionException { // Runnable interface creation thread // new Thread(new Mythread1(), "AA").start(); // Direct use of Callable, error // new Thread(new Mythread2(), "AA").start(); // You can find a class that relates to both Runnable and Callable // Runnable interface has implementation class FutrueTask // The FctrueTask construct also passes Callable // FutureTask // FutureTask<Integer> f1 = new FutureTask<>(new Mythread2()); // new Thread(f1, "AA").start(); // lambda expression writing FutureTask<Integer> f2 = new FutureTask<>(() -> { System.out.println(Thread.currentThread().getName() + " come in Callable"); return 1024; }); // Create a thread new Thread(f2, "jack").start(); // isDone method can be used to determine if it has been completed // while (!f2.isDone()) { // System.out.println("Wait..............."); // } // Call the get method of FutureTask System.out.println(f2.get()); // Re-invoke the execution result and return it directly System.out.println(f2.get()); // Main Thread System.out.println(Thread.currentThread().getName() + " come Over"); /** * FutureTask principle * 1,Do not affect the main thread's tasks, open one thread to execute other threads, do not affect the main thread's tasks * For example: 1. Tasks can be summarized uniformly * There are four classmates: 1 Calculation 1+2+32 calculation: 10+11+12.. 503 calculation: 60+614 calculation: 100+200 * The second student had a large amount of calculation. * FutureTask Open a separate thread to calculate for 2 classmates, first summarize the 1,3,4 classmates, and finally wait for 2 to complete the calculation, unified summary * For example, in the case of an exam, if you make a meeting first, you will not. * * Summary: only once */ } }
8. Powerful JUC auxiliary classes
1. CountDownLatch Decrease Count
-
Summary
- The CountDownLatch class allows you to set a counter, subtract it by 1 using the CountDown method, wait for the counter to be no more than 0 using the await method, and then continue executing the statement following the await method.
- CountDownLatch has two main methods. When one or more threads call the await method, they block
- Other threads calling the countDown method subtracts the counter by one (threads calling the countDown method do not block)
- When the value of the counter changes to 0, the thread blocked by the await method wakes up and continues execution.
Example: The monitor can only lock the door when all six students leave the classroom
// Not with CountDownLatch package com.codetip.codejuc.juc.callable; // Demonstrate CountDownLatch public class CountDownLatchDemo { // (CountDownLatch not applicable) The monitor can lock the door only after six students leave the classroom one after another public static void main(String[] args) { // Six students left the classroom one after another for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " Classmates leave the classroom"); }, i + "Classmate").start(); } System.out.println(Thread.currentThread().getName() + "The monitor has begun to lock the door and walk away."); } }
// Use CountDownLatch package com.codetip.codejuc.juc.callable; import java.util.concurrent.CountDownLatch; // Demonstrate CountDownLatch public class CountDownLatchDemo { // (CountDownLatch not applicable) The monitor can lock the door only after six students leave the classroom one after another public static void main(String[] args) throws InterruptedException { // Set counter to 6 CountDownLatch count = new CountDownLatch(6); // Six students left the classroom one after another for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " Classmates leave the classroom"); // Count minus 1 count.countDown(); }, i + "Classmate").start(); } count.await(); System.out.println(Thread.currentThread().getName() + "The monitor has begun to lock the door and walk away."); } }
2. CyclicBarrier Cyclic Barrier
-
Summary
-
A synchronous auxiliary class that allows a group of threads to wait for each other until a set common barrier is reached, and CyclicBarrier is useful in programs involving a set of fixed-size threads that have to wait for each other from time to time. Because the Barrier can be reused after the waiting thread is released, it is called a circular Barrier.
Example: Among the seven dragon beads: seven must be assembled to summon the Dragon.
-
-
Code implementation:
package com.codetip.codejuc.juc.callable; import java.util.concurrent.CyclicBarrier; // Call the Dragon with 7 Dragon Balls public class CyclicBarrierDemo { // Set Fixed Value private static final int num = 7; public static void main(String[] args) { // Create CyclicBarrier CyclicBarrier cyclikBarrier = new CyclicBarrier(num, () -> { System.out.println("Ha-ha****,Seven dragon beads can summon the Dragon!!!!"); }); // The process of collecting seven dragon beads for (int i = 1; i <= 7; i++) { new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " Star, dragon beads are collected!"); wait for cyclikBarrier.await(); } catch (Exception e) { e.printStackTrace(); } }, String.valueOf(i)).start(); } } }
Run Screenshot
3. Semaphore semaphore
-
concept
- A counted semaphore, conceptually, maintains a license set. If necessary, each acquire() will be blocked before a license is required to be available, and then each release () will add (release) a license. An apricot may release a blocking acquirer, but without using the time of the license object, Semapshore will only count the number of available licenses and Cai Xu will act accordingly.
-
Example: Parking lot. When you get a car, you get a license. The car is full. The parking lot can't enter the car anymore. The others have to wait. When a vehicle leaves and release s a parking space, other vehicles can continue to acquire parking.
Code case:
package com.codetip.codejuc.juc.callable; import java.util.Random; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; // 6 cars, 3 parking spaces public class SemapHore { // Define 3 parking spaces public static final int semaphoreNum = 3; public static void main(String[] args) { // Create a semaphore, set the number of licenses Semaphore semaphore = new Semaphore(semaphoreNum); for (int i = 1; i <= 6; i++) { new Thread(() -> { try { // Random dwell time int wait = new Random().nextInt(5); // Get a license semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " Car number, got the parking space!!"); TimeUnit.SECONDS.sleep(wait); System.out.println(Thread.currentThread().getName() + " -----Car No. 1, out of parking!!,Stopped:" + wait + "second"); } catch (InterruptedException e) { e.printStackTrace(); } finally { // release semaphore.release(); } }, String.valueOf(i)).start(); } } }
Run result:
9. ReentrantReadWriteLock Read-Write Lock
1. Pessimistic Lock
-
When you modify data, you assume that others may modify it, so lock it first, and unlock it when the modification is complete.
- Disadvantages: Concurrency is not supported and efficiency is low
2. Optimistic Lock
-
When you modify data, you think others will not modify it. When you get the data, you will have a version number. When you modify the data, you will change it according to the requirements of this version number. If the version number is inconsistent, others have modified it.
- Supports concurrency,
3. Read-write lock
- Read Lock: Shared Lock
- Write lock: exclusive lock
Both send deadlocks:
Read Lock: There are two read operations, Read 1 and Read 2. Threads can also do other operations while reading. Read 1: Read and write, Read 2: Also reading. Read 1 can't be modified until Read 2 is finished. Read 2 also modifies this, and a deadlock will occur. Read 1 cannot be modified until Read 2 is finished, and Read 2 cannot operate until Read 1 is finished.
Write lock: Thread 1: Write the first record once. Thread 2 performed a write operation on the second record. Thread 1 then modifies the second record. Thread 2 also modifies the first item. A deadlock is sent, and thread 1 and 2 wait for each other to release the lock.
4. Code demonstration:
- No read-write lock
package com.codetip.codejuc.juc.readOrWrite; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; // Resource Class class MyCahe { // Create Map Collection private volatile Map<String, Object> map = new HashMap<>(); // Store data public void put(String key, Object value) { System.out.println(Thread.currentThread().getName() + " Writing+++,Key For:" + key); try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } // Place data map.put(key, value); System.out.println(Thread.currentThread().getName() + " write+++Execution complete, Key For:" + key); } // Read data public Object get(String key) { Object result = null; System.out.println(Thread.currentThread().getName() + " Reading---,Key For:" + key); try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } // Place data result = map.get(key); System.out.println(Thread.currentThread().getName() + " read---Finish execution, Key For:" + key); return result; } } public class ReadWriteLockDemo1 { public static void main(String[] args) { MyCahe myCahe = new MyCahe(); // Create Threads to Put Data for (int i = 1; i <= 3; i++) { final int num = i; new Thread(() -> { myCahe.put(num + "", num + ""); }, String.valueOf(i)).start(); } // Create thread to fetch data for (int i = 1; i <= 3; i++) { final int num = i; new Thread(() -> { myCahe.get(num + ""); }, String.valueOf(i)).start(); } } }
Run result:
2. Add Read-Write Lock
package com.codetip.codejuc.juc.readOrWrite; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; // Resource Class class MyCahe { // Create Map Collection private volatile Map<String, Object> map = new HashMap<>(); //Object to create read-write locks ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // Store data public void put(String key, Object value) { // Add Write Lock readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " Writing+++,Key For:" + key); TimeUnit.MICROSECONDS.sleep(300); // Place data map.put(key, value); System.out.println(Thread.currentThread().getName() + " write+++Execution complete, Key For:" + key); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release Write Lock readWriteLock.writeLock().unlock(); } } // Read data public Object get(String key) { Object result = null; // Add Read Lock readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " Reading---,Key For:" + key); TimeUnit.MICROSECONDS.sleep(300); // Place data result = map.get(key); System.out.println(Thread.currentThread().getName() + " read---Finish execution, Key For:" + key); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release Read Lock readWriteLock.readLock().unlock(); } return result; } } public class ReadWriteLockDemo1 { public static void main(String[] args) { MyCahe myCahe = new MyCahe(); // Create Threads to Put Data for (int i = 1; i <= 3; i++) { final int num = i; new Thread(() -> { myCahe.put(num + "", num + ""); }, String.valueOf(i)).start(); } // Create thread to fetch data for (int i = 1; i <= 3; i++) { final int num = i; new Thread(() -> { myCahe.get(num + ""); }, String.valueOf(i)).start(); } } }
Execution effect:
5. Deep Read-Write Lock
- Read-write lock: A resource can be accessed by multiple read threads at the same time or by a write thread, but there are different read-write threads at the same time. Read-write is mutually exclusive and read-read is shared
The evolution of read-write locks:
First unlock | Second: Add a lock | Third: Read-write lock ReentrantReadWriteLock |
---|---|---|
Multi-threaded grab resources (chaotic) | Using synchronized and ReentrantLock | Read and read can be shared to improve performance, while multiple people can read and operate |
Is exclusive, can only operate one at a time | Write or One | |
Disadvantages: Read one | Disadvantages: 1: Lock hunger (read all the time, no write) | |
Write one | 2: When you read, you cannot write. You can only write when you have finished reading. |
6. Lock Degradation
-
Demotes a write lock to a read lock, with write permissions greater than read permissions.
- You can only downgrade from a write lock to a read lock. Cannot upgrade from a read lock to a read lock
-
Demotion process:
Note: This is the mermaid syntax in MarkDown and the result is the image above
flowchart LR id1[Get Write Lock]-->id2[Acquire Read Lock]--> id3[Release Write Lock]-->id4[Release Read Lock]
package com.codetip.codejuc.juc.readOrWrite; import java.util.concurrent.locks.ReentrantReadWriteLock; // Lock Demotion Demo public class LockDemotion { public static void main(String[] args) { // Create a reentrant lock ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // Write lock ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); // Read Lock ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); try { // Get Write Lock writeLock.lock(); System.out.println("+++++Writing!!!"); // Acquire Read Lock readLock.lock(); System.out.println("-----Reading operation!!!!"); } finally { // Release Write Lock writeLock.unlock(); // Release Read Lock readLock.unlock(); } } }
10. BlockingQueue Blocking Queue
1. Review the data structure first
queue | Stack |
---|---|
FIFO | FIFO |
2. Blocking queue:
- As the name implies, first it is a queue, through which data can be input from one end of the queue and output from the other end.
- When the queue is empty, getting elements from the queue will be blocked until other threads insert new elements into the empty queue
- When the queue is full, adding elements to the queue will be blocked until another thread removes one or more elements from the queue or empties them altogether, leaving the queue idle and adding them later.
In multithreaded environments, so-called blocking suspends the thread (i.e., blocking) in some cases, and once the condition is met, the suspended thread is automatically awakened for execution
Note: This is the mermaid syntax in MarkDown and the result is the image above
flowchart LR Thread1-->|Put|BlockingQueue-->|Take|Thread2
3. Code architecture for blocking queues
java.util.concurrent interface BlockingQueue
-
Type parameters:
E - Element types maintained in this collection
-
All Super Interfaces:
Collection, Iterable, Queue
-
All known subinterfaces:
BlockingDeque
-
All known implementation classes:
ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue
4. Classification of blocking queues
Queue Class Name | explain |
---|---|
ArrayBlockingQueue (commonly used) | Bounded blocking queue composed of array structure |
LinkedBlockingQueue (Common) | Bounded blocking queue composed of a chain table structure (default size: Integer. MAX_VALUE) |
DelayQueue | Delayed unbounded blocking queue using priority queue |
PriorityBlockingQueue | Unbounded blocking queue supporting priority ordering |
SynchronizedQueue | A blocking queue that does not store elements, that is, a queue of individual elements |
LinkedTransferQueue | An unbounded blocking queue consisting of a list of chains |
LinkedBlockingDeque | A two-way blocking queue consisting of a list of chains |
- Core methods of queues
Method Type | throw | Special Values | block | overtime |
---|---|---|---|---|
insert | add(e) | offer(e) | put(e) | offer(time,unit) |
remove | remove(e) | poll() | take() | poll(time,unit) |
inspect | element(e) | peek() | Not available | Not available |
Name | describe |
---|---|
throw | When the queue is full: add element to the queue, throw: java.lang.IllegalStateException: Queue full exception |
When the queue is empty: remove element from the queue, throw: main "java.util.NoSuchElementException exception" | |
Special Values | Insert method: Return true successfully, Fail false, Remove method: Return elements in queue successfully, Return nul inside |
block | When the blocking queue is full, the producer thread continues to put elements into the queue, and the queue will block the producer thread until the putdataor interrupts and exits accordingly |
When the blocking queue is empty, the consumer thread attempts to take elements from the queue, and the queue blocks the consumer thread until the queue is available | |
overtime | When the blocking queue is full, the queue blocks the producer thread for a certain period of time, after which the producer thread exits |
5. Starter Cases
package com.codetip.codejuc.juc.BlockingQueue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class BlockingQueueDemo01 { public static void main(String[] args) throws InterruptedException { // Create a fixed-length rental queue BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3); // Group 1 Cases // System.out.println(blockingQueue.add("a")); // System.out.println(blockingQueue.add("b")); // System.out.println(blockingQueue.add("c")); // System.out.println(blockingQueue.add("d")); // System.out.println(blockingQueue.element()); // System.out.println(blockingQueue.remove()); // Group 2 // System.out.println(blockingQueue.offer("a")); // System.out.println(blockingQueue.offer("b")); // System.out.println(blockingQueue.offer("c")); // System.out.println(blockingQueue.offer("d")); // // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // Group 3 // blockingQueue.put("a"); // blockingQueue.put("b"); // blockingQueue.put("c"); // blockingQueue.put("d"); // blockingQueue.take(); // blockingQueue.take(); // blockingQueue.take(); // blockingQueue.take(); // Group 4 System.out.println(blockingQueue.offer("a", 2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.offer("b", 2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.offer("c", 2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.offer("d", 2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS)); System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS)); } }
11. ThreadPool Thread Pool
1. Overview of thread pools
-
Concepts:
- A thread usage pattern. Too many threads incur overhead that can impact cache locality and overall performance. Thread pools maintain multiple threads and wait for supervisors to assign executable tasks, which avoids the cost of creating and destroying threads when dealing with short tasks. Thread pools not only ensure full utilization of the kernel, but also prevent overscheduling. .
2. Thread pool architecture
Note: This is the mermaid syntax in MarkDown and the result is the image above
classDiagram class Tool class Executors{ } Interface Execure <|-- Interface ExecutorService : inherit Interface ExecutorService <|.. Free Class AbstractExecutoService : Realization Free Class AbstractExecutoService <|-- Implementation Class ThreadPoolExecutor : inherit Implementation Class ThreadPoolExecutor <|.. Implementation Class ThreadPoolExecutor Implementation Class ThreadPoolExecutor <|-- Implementation Class ScheduleThreadPoolExecutor : inherit Interface ExecutorService <|-- Interface ScheduleExecutoService : inherit Interface ScheduleExecutoService <|.. Implementation Class ScheduleThreadPoolExecutor: Realization
3. How thread pools are used
Create a thread pool using the Executors tool class
Method | describe |
---|---|
Executors.newFixedThreadPool | A fixed-size thread pool |
Executors.newSingleThreadExecutor | One task, one task execution, one thread pool, one task. |
Executors.newCachedThreadPool | Thread pools create threads on demand, scalable, strong |
Code demonstration
package com.codetip.codejuc.juc; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo001 { public static void main(String[] args) { // One pool with five threads ExecutorService threadPool = Executors.newFixedThreadPool(5); try { for (int i = 1; i <= 10; i++) { threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + " Business in progress!!!")); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } System.out.println("-------------------------Split Line-----------------------"); // One pool, one route ExecutorService threadSingle = Executors.newSingleThreadExecutor(); try { for (int i = 1; i <= 10; i++) { threadSingle.execute(() -> System.out.println(Thread.currentThread().getName() + " Business in progress!!!")); } } catch (Exception e) { e.printStackTrace(); } finally { threadSingle.shutdown(); } System.out.println("-------------------------Split Line-----------------------"); // Bufferable threads ExecutorService executorService = Executors.newCachedThreadPool(); try { for (int i = 1; i <= 10; i++) { executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " Business in progress!!!")); } } catch (Exception e) { e.printStackTrace(); } finally { executorService.shutdown(); } } }
Thread Pool Bottom Principle
The above method for viewing source discovery using thread pools created by Executors is: ThreadPoolExecutor
4. Seven parameters of thread pool
-
ThreadPoolExecutor parameter description
Class name: ThreadPoolExecutor Explain int corePoolSize Number of resident (core) threads int maximumPoolSize Maximum Threads Long keepAliveTime The thread's lifetime (how long does it take to destroy without using it, keeping the number of core threads) TimeUnit unit Units of survival time BlockingQueue workQueue Blocking Queue ThreadFactory threadFactory Thread Engineering RejectedExecutionHandler handler Rejection Policy
5. Thread pool underlying workflow
-
Execute process
-
First, submit the task to invoke execute, second, the core thread will execute the task; Third, there are so many tasks that the core thread cannot execute at the same time and it will be put in the blocking queue
-
Step 4: If the blocked queue is full, a new thread will be created to perform the task, and the total number of threads will not be greater than the total number of threads set.
-
Step 5: Continue with new tasks and no new threads to create. Rejection policy will be executed.
-
Built-in Resolution Policy
Decisive strategy describe AbortPolicy By default, RejectedExecutionHandler exceptions are thrown directly, organizing the system to function properly CallerRunsPolicy The caller runs "Run a tuning mechanism that does not discard tasks or throw exceptions. Instead, it returns a learning task to the caller, which reduces the flow of new tasks DiscardOldestPolicy Discard the task that has been waiting the longest in the queue, then add the current task to the queue and try to submit the task again DiscardPolicy This policy directly discards tasks that cannot be handled, does not handle them, and does not throw any exceptions. This is the best policy if loss is allowed.
Importantly, creating a thread pool in a project by using ThreadPoolExecutor is not recommended, as is the common Executors tool class. This allows students writing to understand the rules of the thread pool and avoid the risk of resource exhaustion by creating their own thread pool by using ThreadPoolExecutor.
The risks and disadvantages of objects that Executors creates thread pools are as follows
Method Explain FixedThreadPool and SingleThreadExecutor Allowed request queue length is Integer.MAX_VALUE, which can cause a lot of requests to accumulate, resulting in OOM CachedThreadPool and SchduledThreadPool The number of threads allowed to be created is Integer.MAX_VALUE, which may create a large number of threads, resulting in OOM -
-
6. Custom Thread Pool
The code is as follows:
package com.codetip.codejuc.juc.ThreadPool; import java.util.concurrent.*; // Create a custom thread pool public class ThreadPoolDemo { public static void main(String[] args) throws InterruptedException { ExecutorService service = new ThreadPoolExecutor( 5,// Core Thread Size 7, // Maximum Threads 2L, // The thread's lifetime (how long does it take to destroy without using it, keeping the number of core threads) TimeUnit.SECONDS,// Survival time unit new ArrayBlockingQueue<>(3),// Blocking Queue Executors.defaultThreadFactory(), // Thread Engineering new ThreadPoolExecutor.AbortPolicy() // Rejection Policy ); try { for (int i = 1; i <= 10; i++) { service.execute(() -> System.out.println(Thread.currentThread().getName() + " Business in progress!!!")); } } catch (Exception e) { e.printStackTrace(); } finally { service.shutdown(); } } }