1. What are threads and processes? What are their differences and relationships
①. Thread is the basic unit of CPU independent operation and independent scheduling;
②. Process is the basic unit of operating system resource allocation and is the executing application program.
③. The connection between the two:
- Processes and threads are the basic units in which programs run by the operating system.
- The process is the container for the thread, it is the thread that does the actual code execution, and the process serves as the execution environment for the thread.
④. The difference between the two:
- Processes have independent spatial addresses. The crash of one process does not affect other processes in protected mode.
- Threads are just different execution paths for a process. Threads have their own stack and local variables, but there is no separate address space between threads. A thread death can affect the process.
- A process is a running application, and a thread is a piece of code that executes within a process. A program contains at least one process, and a process contains at least one thread. Multiple threads in a process share resources owned by the current process.
2. What are the differences between threads and processes
- A process is the smallest unit of resource allocation and a thread is the smallest unit of program execution.
- Processes have their own independent address space, and each time a process is started, the system allocates the address space to it and sets up tables to maintain code segments, stack segments, and data segments, which is very expensive.
- Threads share data in a process and use the same address space, so it is much less expensive for the CPU to switch a thread than a process, and it is much less expensive to create a thread than a process.
- Communication between threads is more convenient, threads under the same process share global variables, static variables and other data.
- Communication between processes needs to take place by means of communication (IPC). However, how to deal with synchronization and mutual exclusion is the difficulty of writing multithreaded programs.
- Multithreaded programs are more robust. If one thread dies, the entire process dies, while the death of one process does not affect the other process, because the process has its own address space.
3. There are several different ways to create threads in Java
- Thread class inheritance
Public class MyThead extends Thread {@override public void run() {system.out.println (" Thread "+ Thread.currentThread().getName()); Public static void main(String[] args) {MyThead thead1 = new MyThead(); MyThead thead2 = new MyThead(); thead1.start(); thead2.start(); System.out.println(" mainthread "+ thread.currentThread ().getName()); } is implemented by inheritance. The implementation is simple, but there are few application scenarios. Java does not support multiple inheritance.Copy the code
- Implement Runnable
Public class MyThead1 implements Runnable {@override public void run() {system.out.println (" implements "+ Thread.currentThread().getName()); Public static void main(String[] args) {MyThead1 thead1 = new MyThead1(); MyThead1 thead2 = new MyThead1(); thead1.run(); thead2.run(); System.out.println(" mainthread "+ thread.currentThread ().getName()); } the most common way, simple implementation, basic any scene can be used.Copy the code
- Create threads through Callable and Future
public class MyThead2 implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println(" Thread "+ thread.currentThread ().getName()); return 0; Public static void main(String[] args) {MyThead2 thead1 = new MyThead2(); MyThead2 thead2 = new MyThead2(); FutureTask<Integer> result1 = new FutureTask<>(thead1); new Thread(result1).start(); Try {system.out.println (" thread result "+ result1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } FutureTask<Integer> result2 = new FutureTask<>(thead2); new Thread(result2).start(); Try {system.out.println (" thread result "+ result2.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } system.out.println (" mainthread "+ thread.currentThread ().getName()); } for special scenarios, if the created thread needs to return a value, but using FutureTask to retrieve the return value will cause the main thread to block.Copy the code
4. Several states of threads
- New state (new) : A new thread object is created.
- Runnable: After a thread object is created, another thread (such as the main thread) calls the start() method of the object. Threads in this state are in the runnable thread pool, waiting to be selected by thread scheduler for CPU usage.
- Running: A thread in a runnable state gets a TIMESlice and executes program code.
- Block: A block is when a thread has relinquished CPU usage for some reason, giving up CPU timeslice, and temporarily stops running. Until the thread enters the runnable state, it has no chance to get the CPU timeslice again and go to the running state.
There are three ways to block: 1. Wait to block: The running thread executes o.wait() and the JVM places the thread in waitting queue. Synchronization blocking: When a running thread obtains a synchronization lock on an object and the lock is held by another thread, the JVM places that thread into a lock pool. 3. Other blocking: When a running Thread executes thread.sleep (long ms) or t.join (), or makes an I/O request, the JVM puts the Thread in a blocked state. When the sleep() state times out, when the join() wait thread terminates or times out, or when I/O processing is complete, the thread returns to the runnable state.
- Dead: the run() and main() methods of a thread end, or exit the run() method abnormally, the thread ends its life cycle. Dead threads cannot be resurrected.
5. What are deadlocks and how can they be avoided
- Deadlock: A deadlock occurs when two processes are waiting for each other to complete before proceeding. The result is that both processes are stuck in an infinite wait.
- There are four necessary conditions for multiple threads to produce deadlocks:
1. Mutually exclusive condition: A resource can be used by only one process at a time. 2. Hold and request conditions: when a process is blocked by requesting resources, it holds on to acquired resources. 3. Inalienable conditions: The process has acquired resources and cannot be deprived until they are used. 4. Circular waiting condition: a circular waiting resource relationship is formed between several processes.
- Avoid deadlocks: just break any condition
6. What is the difference between the start() and run() methods in Thread
- A thread is started by calling the start() method of the thread class, making it ready to be scheduled for execution by the JVM. During scheduling, the JVM calls the run() method of the thread class to complete the actual business logic. When the run() method ends, the thread terminates.
- If you call the run() method of the thread class directly, it is treated as a normal function call, and there is still only one thread in the program, the main thread. That is, the start() method can call the run() method asynchronously, but directly call the run() method is synchronous, cannot achieve the purpose of multithreading.
- Therefore, you can achieve multithreading only by calling the start() method of the thread class.
7. How to stop a thread in Java
JDK 1.0 originally had some control methods like stop(), suspend(), and resume(), but due to potential deadlock threats. They were therefore deprecated in subsequent JDK versions, after which the Designers of the Java API did not provide a compatible and thread-safe way to stop a thread. The thread terminates automatically when the run() or call() methods are complete. To terminate a thread manually, use volatile Boolean variables to exit the run() loop or cancel the task to interrupt the thread. Let me start with two concepts: 1. Lock pool: Suppose thread A already owns A lock on an object (note: not A class) and another thread wants to call A synchronized method (or block) on that object. Since these threads must first acquire ownership of the object’s lock before entering the synchronized method of the object, but the lock of the object is currently owned by thread A, these threads enter the lock pool of the object. 2. Wait pool: If thread A calls wait() on an object, thread A releases the lock on the object and enters the wait pool for that object. Notify and notifyAll 1. If a thread calls wait() on an object, it is in the wait pool of the object. The threads in the wait pool do not compete for the lock. 2. When a thread invokes notifyAll() or notify() of an object, the awakened thread enters the lock pool of the object. The threads in the lock pool compete for the lock. That is, after notify is called, only one thread enters the lock pool from the wait pool. NotifyAll moves all threads in the wait pool to the lock pool and waits for locks to compete. If a thread does not compete for the lock, it will remain in the lock pool until the thread calls wait() again. The thread that contended for the object lock continues to execute until the synchronized block is finished, which releases the object lock, at which point the pool of threads continues to contend for the object lock.
What is a thread pool
- Thread pooling is a form of multi-threaded processing in which tasks are submitted to a thread pool and the execution of tasks is managed by the thread pool. If a thread is created to handle each request, the server’s resources are quickly exhausted. Using thread pools reduces the number of threads created and destroyed, and each worker thread can be reused to perform multiple tasks.
Common thread pools and usage scenarios
- newSingleThreadExecutor
Create a single threaded thread pool that uses only one worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).
// Create a thread pool with only one thread, and the thread lifetime is unlimited; Public static void singleTheadPoolTest() {corePoolSize = 1; corePoolSize = 1; MaximumPoolSize to 1; KeepAliveTime of 0 l; Unit is: timeunit.milliseconds; WorkQueue is: the new LinkedBlockingQueue < Runnable > () unbounded blocking queue ExecutorService pool = Executors. NewSingleThreadExecutor (); for (int i = 0; i < 10; i++) { final int ii = i; pool.execute(() -> out.println(Thread.currentThread().getName() + "=>" + ii)); Thread name: pool-1-thread-1, execute 0 thread name: pool-1-thread-1, execute 1 Thread name: pool-1-thread-1, execute 2 Thread name: pool-1-thread-1, execute 3 Thread name: Pool-1-thread-1, go to 4. Name of the thread: pool-1-thread-1, go to 5. Name of the thread: pool-1-thread-1, go to 6. If pool-1-thread-1 is used, go to 8. If thread name is pool-1-thread-1, go to 9Copy the code
- newFixedThreadPool
A fixed size thread pool can be used to limit the number of threads given the concurrency pressure.
// Create a pool that can hold a fixed number of threads. Each thread lives for an unlimited time. Public static void fixTheadPoolTest() {public static void fixTheadPoolTest() {public static void fixTheadPoolTest() { CorePoolSize is nThread, maximumPoolSize is nThread. KeepAliveTime is 0L(unlimited); Unit is: timeunit.milliseconds; WorkQueue is: New LinkedBlockingQueue < Runnable > () unbounded blocking queue ExecutorService fixedThreadPool = Executors. NewFixedThreadPool (3); for (int i = 0; i < 10; i++) { final int ii = i; Fixedthreadpool.execute (() -> {out.println(" thread.currentThread ().getName() + ", execute "+ ii); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }}); Thread name: pool-1-thread-3, go to 2 thread name: pool-1-thread-1, go to 0 Thread name: pool-1-thread-2, go to 3 Thread name: pool-1-thread-3, go to 4 If the thread name is pool-1-thread-1, go to 5. If the thread name is pool-1-thread-2, go to 6. If the thread name is pool-1-thread-3, go to 7Copy the code
- newCachedThreadPool
Create a cacheable thread pool. If the length of the thread pool exceeds the processing requirement, you can recycle idle threads flexibly, or create a new thread if none is available.
When a new task arrives, it is inserted into the SynchronousQueue. Because SynchronousQueue is a SynchronousQueue, it looks for available threads to execute, or creates a thread to execute the task if no threads are available. If a thread in the pool is idle for more than a specified amount of time, the thread is destroyed. Public static void cacheThreadPool() {corePoolSize is 0; MaximumPoolSize Integer. MAX_VALUE; KeepAliveTime of 60 l; The unit for TimeUnit. SECONDS; WorkQueue SynchronousQueue will (synchronous queue) ExecutorService cachedThreadPool = Executors. NewCachedThreadPool (); for (int i = 1; i <= 10; i++) { final int ii = i; try { Thread.sleep(ii * 1); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(()->out.println(" thread.currentThread ().getName() + ", execute "+ ii)); Thread name: pool-1-thread-1, execute 1 Thread name: pool-1-thread-1, execute 2 Thread name: pool-1-thread-1, execute 3 Thread name: pool-1-thread-1, execute 4 Thread name: Pool-1-thread-1, go to 5. Name of the thread: pool-1-thread-1, go to 6. Name of the thread: pool-1-thread-1, go to 7. If pool-1-thread-1 is used, go to 9. If thread name is pool-1-thread-1, go to 10Copy the code
- newScheduledThreadPool
Create a fixed – length thread pool to support scheduled and periodic task execution.
// Create a thread pool with a fixed size. The thread pool can support the execution of scheduled and periodic tasks. If all threads are busy, new tasks will be queued to DelayedWorkQueue. Public static void sceduleThreadPool() {//corePoolSize is the passed parameter, maximumPoolSize is integer.max_value; KeepAliveTime of 0; Unit is: timeunit.nanoseconds; WorkQueue is: New DelayedWorkQueue() a queue that is sorted in ascending order by timeout ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); Runnable r1 = () -> out.println(" Thread name: + thread.currentThread ().getName() + ", execute :3 seconds later "); scheduledThreadPool.schedule(r1, 3, TimeUnit.SECONDS); Runnable r2 = () -> out.println(" Thread name: "+ thread.currentThread ().getName() +", execute every 3 seconds after 2 seconds delay); scheduledThreadPool.scheduleAtFixedRate(r2, 2, 3, TimeUnit.SECONDS); Runnable r3 = () -> out.println(" Thread name: + thread.currentThread ().getName() + ", execute: common task "); for (int i = 0; i < 5; i++) { scheduledThreadPool.execute(r3); }} / / name of threads run results: - thread pool - 1-1, execution: ordinary task thread title: - thread pool - 1-5, execution: ordinary task thread title: - thread pool - 1-4, execution: ordinary task thread name: - thread pool - 1-3, perform: ordinary task thread title: - thread pool - 1-2, execution: ordinary task thread title: - thread pool - 1-1, execution: after 2 seconds delay once every 3 seconds to perform the thread title: Thread name: pool-1-thread-5, execution :3 seconds later Thread name: pool-1-thread-4, execution: every 3 seconds after 2 seconds delay Thread name: Pool-1-thread-4, execution: every 3 seconds after 2 seconds delay Thread name: Every 3 seconds after 2 seconds delay Thread name: pool-1-thread-4. Execution: If the delay is 2 seconds, the execution is performed every 3 secondsCopy the code
10. Several important parameters in the thread pool
- corePoolSize
The number of core threads in the thread pool, these few core threads, are not recycled, only when they are not used
- maximumPoolSize
The maximum number of threads that can be held in a thread pool
- keepAliveTime
The maximum amount of time that any thread in the thread pool other than the core thread can be retained, because in the thread pool, all threads except the core thread can not be eliminated even if there is no task, the rest are “live”, meaning the maximum idle time that a non-core thread can remain.
- util
Measure this time in one unit.
- workQueue
Wait queue. Tasks can be stored in a task queue to be executed. FIFIO (First in, first out) is implemented.
- threadFactory
Create thread factories for threads.
- handler
A rejection strategy where we refuse to perform certain tasks when they are full.
11. Wait queue for thread pool
- ArrayBlockingQueue
A bounded blocking queue based on an array structure that sorts elements in FIFO (first-in, first-out).
- LinkedBlockingQueue
A linked list-based blocking queue that sorts elements in FIFO (first in, first out) and typically has a higher throughput than ArrayBlockingQueue. Static factory methods Executors. NewFixedThreadPool () using the queue
- SynchronousQueue
A blocking queue that does not store elements. Each insert operation must wait until another thread calls to remove operation, otherwise the insert has been in the blocking state, the throughput is usually more than LinkedBlockingQueue, static factory methods Executors. NewCachedThreadPool using the queue.
- PriorityBlockingQueue
An infinite blocking queue with priority.
12. Thread pool rejection policy
When requests keep coming and the system cannot handle them at this time, the policy we need to adopt is denial of service. There are already four processing strategies in ThreadPoolExecutor.
- AbortPolicy strategy
This policy simply throws an exception and prevents the system from working properly.
- CallerRunsPolicy strategy
This policy runs the current discarded task directly in the caller thread, as long as the thread pool is not closed.
- DiscardOleddestPolicy strategy
This policy discards the oldest request, the task that is about to be executed, and attempts to submit the current task again.
- DiscardPolicy strategy
This policy silently discards unprocessed tasks and leaves nothing to be processed. In addition to the four default RejectedExecutionHandler interfaces provided by the JDK, you can customize the RejectedExecutionHandler interface based on your service requirements.
13. How to close the thread pool and select the number of threads when initializing the thread pool
- Shutting down the thread pool can be done by calling the shutdownNow and shutdown methods
ShutdownNow: Interrupt () is issued for all ongoing tasks, stops execution, cancels all tasks that have not yet started, and returns a list of tasks that have not yet started. Shutdown: When we call shutdown, the thread pool will not accept new tasks, but will not force the termination of committed or executing tasks.
- Thread count selection
If the task is IO intensive, the number of threads should be more than twice the number of cpus to maximize the utilization of CPU resources. If the task is CPU-intensive, you only need to increase the number of threads by 1. Increasing the number of threads can only increase context switching, but not CPU utilization. The above is just a basic idea, but if you really need precise control, you still need to watch the number of threads in the thread pool and the queue after going live.