Follow me for the latest knowledge, classic interview questions and technical sharing
Multithreading and concurrency are essential knowledge points in job interviews, which involve many points and are very difficult. Some people face these problems a little confused, in order to solve this situation, summarized the basic knowledge of Java multi-threaded concurrency. Moreover, in order to further study Java multi-threaded concurrency, we must first master the basic knowledge, which can be well prepared for the subsequent in-depth study of each module. Synchronize synchronize, a common thread pool, Lock, AQS, and concurrent container source code.
1 Basic Concepts
program: a collection of computer instructions stored as a file on disk, i.e. a program is static code
process:
- Is an execution activity of a program in its own address space. It is the basic unit of system execution program
- A process is a unit of resource requisition, scheduling, and independent operation
thread:
- A single continuous flow of control in a process. A process can have multiple threads.
- Thread is also known as lightweight process, it and process have independent execution control, the operating system is responsible for scheduling, the difference is that threads do not have independent storage space, but with the process of other threads to share a storage space, which makes the communication between threads is far simpler than the process.
The relationship between the three:
- Threads are smaller running units that processes are divided into. The main difference between threads and processes is that processes are essentially independent, whereas threads are not necessarily, as threads in the same process will most likely interact with each other.
- On the other hand, a process belongs to the category of operating system, mainly in the same period of time, can execute more than one program, while a thread is in the same program almost simultaneously execute more than one program segment.
The JVM (Memory, garbage collection, performance optimization) can be used to solve interview problems.
2 Thread Composition
Components: virtual CPU, code executed, and data processed.
3 Differences between threads and processes
process: refers to the running application program in the system, which has its own independent memory space.
thread: refers to an execution process in a process. Multiple threads are allowed to be started at the same time in a process. They perform different tasks respectively.
Main differences:
- Each process requires the operating system to allocate a separate memory address space for it
- And all threads in the same process in the same address space, these threads can share data, so the communication between threads is relatively simple, the consumption of system overhead is relatively small
Why multithreading
Multithreading benefits:
- Multiple tasks can be performed concurrently
- When a functional part of the program is waiting for some resources and does not want to suspend the program because of waiting, it can create another thread to do other work;
- Multithreading can reduce the IDLE time of CPU to the maximum extent, thus improving the utilization rate of CPU;
5 the main thread
When a Java program starts, a thread runs immediately, executing the main method. This thread is called the main thread of the program. Any Java program has at least one thread, the main thread.
Main thread is special in that:
- It is the thread that produces the child threads of other routines;
- Usually it must end last because it performs the closing of other child threads.
6 Thread priority
A single-core computer has only one CPU, and each thread takes turns acquiring the CPU to perform a task:
- Threads with higher priorities have more chances to get cpus, and vice versa;
- The priority is an integer ranging from 1 to 10. Generally, it is the default value of the thread
- The priority is 5, but the setPriority and getPriority methods can also set or return the priority;
Thread
Class has the following three static constants to indicate priority:
- MAX_PRIORITY: the value is 10, indicating the highest priority
- MIN_PRIORITY: the value is 1, indicating the lowest priority
- NORM_PRIORITY: the value is 5, indicating the default priority
7 Thread life cycle
Restoring thread state (State
Enumeration values represent thread state) :
- NEW State (NEW)The thread has just been created and has not been started.
Thread thread = new Thread()
. - RUNNABLE:After the thread object is created, other threads (such as the main thread) call the object’s
start
Methods. Threads in this state are in the runnable thread pool, waiting to be selected by thread scheduler for CPU usage. - Run (running) :Thread obtains CPU resources and is performing tasks.
run()
Unless the thread automatically abandons CPU resources or a higher priority thread enters, the thread will run until the end - Blocked:A thread is suspended while it is running, usually to wait for a certain time to occur (such as when a resource is ready) before continuing.
sleep
.suspend
.wait
Etc can cause a thread to block - WAITING: a thread in this state is WAITING for some specific action (notification or interrupt) from another thread.
- TIMED_WAITING:This state is different from
WAITING
, it can return after a specified time. - Termination (TERMINATED) :Indicates that the thread has finished executing if a thread’s
run
Method completes execution or is calledstop
Method, the thread dies. For dead threads, no longer availablestart
Method to get it ready.
(3) A thread may be Blocked while Running:
- call
join()
andsleep()
Method,sleep()
When time ends or is interrupted,join()
Interrupt,I/O completion will returnRunnable
State, waiting for the JVM to schedule. - call
wait()
, making the thread in the wait blocked pool untilnotify()
/notifyAll()
The thread is awakened and locked in a locked pool, and the synchronization lock is released to return the thread to a Runnable state. - A thread in the Running state is locked in a blocked pool by Synchronized, and the Synchronized lock is released into a Runnable state.
8 Thread creation mode
(3) Thread creation method:
- Implement Runnable interface, overload
run()
, no return value - Inheriting Thread class, overwrite
run()
- Implement the Callable interface, using FutureTask/Future to create threads that return values and execute them through Executor
- Create ExecutorService by using Executors and log in to Callable or Future
1. Implement Runnable interface, overloadrun()
The Runnable interface exists mainly to solve the problem that Java does not allow multiple inheritance.
public class ThreadRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class ThreadMain {
public static void main(String[] args) throws Exception {
ThreadRunnable threadRunnable1 = new ThreadRunnable();
ThreadRunnable threadRunnable2 = new ThreadRunnable();
ThreadRunnable threadRunnable3 = new ThreadRunnable();
Thread thread1 = new Thread(threadRunnable1);
Thread thread2 = new Thread(threadRunnable2);
Thread thread3 = new Thread(threadRunnable3);
thread1.start();
thread2.start();
thread3.start();
}
}
Copy the code
2. Inherit the Thread class and rewriterun()
By calling Thread’sstart()
Will call the create threadrun()
The code in the run method of different threads is executed alternately. But Java does not support multiple inheritance. Therefore, inheriting Thread means that the subclass cannot inherit from other classes.
public class ThreadCustom extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread() + ":" + i); } } } public class ThreadTest { public static void main(String[] args) { ThreadCustom thread = new ThreadCustom(); thread.start(); }}Copy the code
3. Implement the Callable interface to create a Thread with a return value through FutureTask/Future. Execute it through Executor.
public class ThreadCallableCustom { public static void main(String[] args) throws Exception { FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() { public Integer call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return 1; }}); Executor executor = Executors.newFixedThreadPool(1); ((ExecutorService) executor).submit(futureTask); System.out.println(thread.currentThread ().getName() + ":" + futureTask.get()); }}Copy the code
4. Use Executors to create ExecutorService, and input Callable or Future for thread pools and concurrency
public class ThreadExecutors { private final String threadName; public ThreadExecutors(String threadName) { this.threadName = threadName; } private ThreadFactory createThread() { ThreadFactory tf = new ThreadFactory() { public Thread newThread(Runnable r) { Thread thread = new Thread(); thread.setName(threadName); thread.setDaemon(true); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return thread; }}; return tf; } public Object runCallable(Callable callable) { return Executors.newSingleThreadExecutor(createThread()).submit(callable); } public Object runFunture(Runnable runnable) { return Executors.newSingleThreadExecutor(createThread()).submit(runnable); } } public class ThreadTest { public static void main(String[] args) throws Exception { ThreadExecutors threadExecutors = new ThreadExecutors("callableThread"); threadExecutors.runCallable(new Callable() { public String call() throws Exception { return "success"; }}); threadExecutors.runFunture(new Runnable() { public void run() { System.out.println("execute runnable thread."); }}); }}Copy the code
9 Difference between Runnable interface and Callable interface
1) The two interfaces need to implement different method names, Runnable needs to implement the run() method, Callable needs to implement the call() method. 2) The return value of the implemented method is different. There is no return value after the execution of Runnable task, and the result of asynchronous calculation can be obtained after the execution of Callable task. Runnable cannot throw an exception. Callable can throw an exception.
10 Thread Safety
Thread safety definition
A class (object or method) is thread-safe if it consistently behaves correctly when accessed by multiple threads.
Thread safety example
Hunchman singleton pattern – Thread safety
public class EagerSingleton(){ private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton(){}; public static EagerSingleton getInstance(){ return instance; }}Copy the code
How to solve the thread safety problem?
This can be done by locking:
- Synchronized code blocks: Simply place the code that operates on shared data in synchronized
- Synchronized methods: Extract the code that operates on shared data and place it in a synchronized method
- Lock Lock: The synchronization Lock is added
lock()
And release the synchronization lockunlock()
What are deadlocks and live locks?
Deadlock refers to the phenomenon that two or more processes (or threads) are waiting for each other during execution because they are competing for resources. They will not be able to proceed without external forces.
A live lock, where the task or performer is not blocked, keeps trying, failing, trying, failing because some condition is not met.
Necessary conditions for deadlock:
- Mutual exclusion conditions: Mutual exclusion is when a process monopolizes resources for a certain period of time.
- Request and hold conditions: when a process is blocked by requesting resources, it holds on to acquired resources.
- Non-dispossession condition: a process has acquired a resource and cannot take it away until it is used up.
- Circular waiting condition: a circular waiting resource relationship is formed between several processes.
Deadlock resolution:
- Undo all processes that are deadlocked.
- Undo deadlocked processes one by one until the deadlock does not exist.
- Force the abandonment of occupied resources from a deadlocked process one by one until the deadlock is gone. Remove a deadlock state by forcibly depriving another process of enough resources to allocate to a deadlocked process.
What is the pessimistic lock and optimistic lock?
1) Pessimistic lock
Pessimistic locking, always assuming the worst, every time you go to get the data you think someone else is going to change it, so every time you go to get the data you lock it, so that someone else tries to get the data and it blocks until it gets the lock.
- Traditional relational database inside used a lot of this locking mechanism, such as row lock, table lock, read lock, write lock, etc., are in the operation before the first lock.
- Java’s implementation of the synchronized keyword is also pessimistic locking.
2) Optimistic lock
Optimistic lock, as the name implies, is very optimistic, every time to get the data will think that others will not modify, so will not lock, but in the update will determine whether others have to update the data during this period, you can use the version number and other mechanisms. Optimistic locking is suitable for multi-read applications to improve throughput.
Concurrency control of locks between multiple threads
Concurrency control of locks between multiple threads, object locks multiple threads, each thread holds the lock on the object that the method belongs to, and class locks. Synchronized, wait, and notify are synchronization tools available to any object
Synchronization and asynchrony of object locks
- Synchronized: the concept of synchronization is sharing. Only the shared resources need to be considered.
- “Asynchronization” is the concept of being independent and having no restrictions on one another.
Synchronization is for thread safety. In fact, for thread safety, two characteristics need to be met: atomicity (synchronization) and visibility.
14 Volatile keyword
Volatile enables variables to be visible across threads, ensures memory visibility, and prevents instruction reordering
Multithreaded memory model: Main Memory, working
Memory (thread stack), when processing data, the thread loads the value from main memory to the local stack and then saves it back. (Volatile is used to fire a load and save for each operation on that variable.)
15 ThreadLocal
Thread-local variables, by means of space for time, provide an independent copy of variables for each thread, in order to ensure thread safety without locking. The main solution is to let each thread finish executing before terminating, which uses the join() method.
Application scenarios:
- Locking performance is better when concurrency is not high
- In high concurrency scenarios, using ThreadLocal can reduce lock contention to some extent.
Multithread synchronization and mutually exclusive implementation method
1). Thread synchronization refers to a restriction relationship between threads. The execution of one thread depends on the message from another thread.
Synchronization methods between threads can be broadly divided into two types: user mode and kernel mode. As the name suggests:
Kernel mode refers to the use of the system kernel object singleness for synchronization, the use of the need to switch between the kernel state and user state. Methods in kernel mode are:
- The event
- A semaphore
- The mutex
In user mode, operations are performed only in user mode without switching to kernel mode. Methods in user mode include:
- Atomic operations (such as a single global variable)
- A critical region
2). Threads are mutually exclusive, which refers to the exclusive access of individual threads to shared process system resources.
When several threads are using a shared resource, only one thread is allowed to use it at any time, and other threads must wait until the hogger releases the resource. Thread mutex can be thought of as a special kind of thread synchronization.
17 Communication between threads
Thread is an independent individual in the operating system, but these individuals can not become a whole without special cooperation, communication between threads has become one of the necessary ways of the whole.
How many ways can threads communicate with each other?
Communication between threads:
- The Shared memory
- The messaging
Shared memory: In the concurrent model of shared memory, threads share the common state of the program and communicate implicitly with each other by writing to read the common state in memory. The typical shared memory communication mode is to communicate through shared objects.
Messaging: In the concurrent model of messaging, there is no common state between threads, and threads must communicate explicitly by explicitly sending messages. In Java, the typical way to pass messages iswait()
和 notify()
Or BlockingQueue.
18 What is the Java Lock interface?
Java. Util. Concurrent. The locks. Lock interface, provide more than synchronized to expand the Lock operation. It allows for a more flexible structure, can have completely different properties, and can support conditional objects of multiple related classes. Its advantages include:
- Can make locks fairer.
- Threads can be made to respond to interrupts while waiting for locks.
- You can have a thread attempt to acquire the lock and either return immediately or wait a certain amount of time if the lock cannot be acquired.
- Locks can be acquired and released in different order in different scopes.
19 Java AQS
AQS, AbstractQueuedSynchronizer, namely queue synchronizer. It is the basic framework for building locks or other synchronization components (such as ReentrantLock, ReentrantReadWriteLock, Semaphore, etc.), and the author of the J.U.C. and package (Doug Lea) expects it to be the basis for most synchronization requirements. It is the core foundational component in J.U.C. and packages.
advantage:
AQS solves many of the details involved in implementing synchronizers, such as obtaining synchronization status and FIFO synchronization queues. There are many benefits to building synchronizers based on AQS. Not only does it greatly reduce the implementation effort, but it also does not have to deal with the problem of competing in multiple locations.
In a synchronizer built on AQS, blocking occurs at only one moment, reducing the overhead of context switching and improving throughput. AQS are also designed with scalability in mind, so all synchronizers built on AQS in J.U.C can gain this advantage.
20 Synchronize the class container
What is a synchronous container? A container that can be understood simply as synchronized, and if more than one thread calls the synchronized container’s methods, they are executed serially.
features:
- Thread-safe
- Some scenarios may require locking to protect compound operations
Common synchronization containers:
- Such as Vector, HashTable
- Use JDK factory methods such as collections.synchronized to create implementations.
- At the bottom, methods are synchronized with the traditional synchronized keyword.
- Performance requirements in high concurrency scenarios cannot be met
21 Concurrent class container
Jdk5.0 provides a variety of concurrent class containers to replace synchronous class containers to improve performance.
Synchronous class container limitations:
- It’s all serialized.
- While thread safety is achieved, concurrency is reduced
- In multithreaded environments, application throughput is severely reduced.
Common concurrent class containers:
- ConcurrentHashMap
- Copy – On – Write container
ConcurrentHashMap principle
- Internally, we use segments to represent these different parts
- Each segment is equivalent to a small HashTable with its own lock.
- Divide a whole into 16 segments. That is, up to 16 threads can be concurrently modified.
- This is also a solution to reduce lock contention by reducing the granularity of locks in multi-threaded scenarios.
Copy – On – Write container
Copy-on-write, for short, is an optimization strategy used in program design.
- Separate reading and writing, separate reading and writing
- Final consistency
- Use alternate space ideas to resolve concurrency conflicts
There are two types of COW containers in JDK:
- CopyOnWriteArrayList: Suitable for scenarios where reads far outnumber writes, such as caching.
- CopyOnWriteArraySet: a thread-safe, unordered collection that can be thought of as a thread-safe HashSet, suitable for sets that are usually kept small, read-only operations far outnumber mutable operations, and need to prevent collisions between threads during traversal
22 the concurrent Queue
concurrent Queue:
- ConcurrentLinkedQueue, a high performance queue, is an appropriate choice when many threads share access to a common collection.
- BlockingQueue, a BlockingQueue, is a queue that supports two additional operations and is often used in producer and consumer scenarios.
ConcurrentLinkedQueue
- Applicable to high concurrency scenarios
- The lock – free method is used to achieve high performance under high concurrency
- It performs better than BlockingQueue
- Follow the first in, first out principle
Common methods:
- Add() and offer() are both methods of adding elements
- Poll() and peek() are both header element nodes, the difference being that Poll() removes elements and peek() does not.
BlockingQueue interface implementation:
- ArrayBlockingQueue: ArrayBlockingQueue is an array-based implementation of a block queue that maintains an array of fixed length to cache data objects in the queue. There is no read-write separation internally, meaning that production and consumers cannot be completely parallel.
- LinkedBlockingQueue: LinkedBlockingQueue, like ArrayBlockingQueue, maintains an internal data buffer queue (which is made up of a single list). LinkedBlockingQueue can handle concurrent data efficiently because its internal implementation uses separate locks (read and write separate two locks). This enables producer and consumer operations to run completely in parallel.
- PriorityBlockingQueue: A priority-based blocking queue (the priority is determined by the Compator object passed into the constructor, which means that objects passed into the queue must implement the Comparable interface). When implementing PriorityBlockingQueue, the internal control thread synchronization locks are fair locks
- DelayQueue: a Queue with a delay time in which the element can only be fetched from the Queue when the specified delay time is up. The elements in the DelayQueue must implement the Delayed interface first. DelayQueue is a queue with no size limit and can be applied in many scenarios, such as the removal of cached timeout data, task timeout processing, closing of idle connections, and so on.
- SynchronousQueue: An unbuffered queue in which data generated by producers is directly retrieved and consumed by consumers immediately
Multithreaded design pattern
- Evolved based on parallel design patterns
- Part of the design optimization
- Is a summary and abstraction of some commonly used multithreaded structures
- What are the common multithreaded design patterns?
24 Concurrent.util Common classes
CountDownLatch: Used to listen for certain initialization operations and notify the main thread to continue working after initialization.
CycilcBarrier: when all threads are ready, start together. If one thread is not ready, everyone waits.
The common concurrent. util class is defined to implement asynchronous callback. The JDK provides an implementation package for this scenario, simplifying the call to . It is suitable for scenarios where time-consuming service logic is processed, effectively reducing system response time and improving system throughput.
Concurrent.util Common class Semaphore: Semaphore, suitable for high Concurrent access, used for access traffic control
ReentrantLock. Add a lock to the part of the code that needs to be synchronized, but don’t forget to release the lock at the end, otherwise the lock will never be released and other threads will never be able to access it.
Lock and wait/notification
- Object wait(), notify(), and notifyAll() methods are required to cooperate with each other when multi-threads cooperate
- When using locks, you can use a new wait/notification class called Condition
- The Condition is specific to a particular lock
Condition more
- Multiple conditions can be generated from a Lock object to interact with multiple threads
- Causes some of the threads that need to be woken up to wake up, while others continue to wait for notification.
ReentrantReadWriteLock(Read/write lock)
- At its core is the lock that implements read/write separation. In particular, the performance is much better than reentrant locking in the case of high concurrent access with more read and less write.
- Separate read and write locks into read and write locks
- Under a read lock, multiple threads can access concurrently
- Under write locks, only sequential access is possible one by one
- Lock the optimization
25 the thread pool
Reasons to use the Executor framework:
- New threads () are created for each task. Creating a Thread is time consuming and resource consuming.
- The threads created by calling new threads () are poorly managed, called wild threads, and can be created without limit. Competition between threads will lead to excessive use of system resources, resulting in system breakdown, and frequent alternation between threads will consume a lot of system resources.
- Connecting threads started with new threads () is not scalable, such as scheduled execution, scheduled execution, scheduled execution, Thread interruption, etc
thread pool creation method:
Common task thread pool
- The newFixedThreadPool(int nThreads) method creates a fixed length thread pool.
- Each time a task is submitted, a thread is created until the maximum number of threads in the pool is reached, at which point the thread size does not change.
- When a thread terminates with an unexpected error, a new thread is added to the thread pool.
- The newCachedThreadPool() method creates a cacheable thread pool.
- If the size of the thread pool exceeds processing requirements, idle threads are automatically reclaimed.
- When demand increases, new threads can be added automatically. There is no limit to the size of the thread pool.
- The newSingleThreadExecutor() method creates a single-threaded thread pool.
- It creates a single worker thread to execute the task, and if that thread terminates abnormally, a new one is created to replace it.
- It is characterized by ensuring that tasks are executed sequentially according to the order in the queue.
Scheduled task thread pool
- The newScheduledThreadPool(int corePoolSize) method creates a fixed-length thread pool and executes tasks in a deferred or timed manner, similar to a Timer.
- The newSingleThreadExecutor() method creates a thread pool of fixed length 1 and executes tasks in a deferred or timed manner, similar to a Timer.
How to close the thread pool
ThreadPoolExecutor provides two methods for closing a thread pool:
- The shutdown() method does not terminate the thread pool immediately, but does not terminate until all tasks in the task cache queue have completed, but no new tasks are accepted.
- The shutdownNow() method immediately terminates the thread pool and attempts to interrupt the task in progress, and empties the task cache queue to return the task that has not yet been executed
26 summary
Because Java multithreading concurrency involves too many knowledge points, here is not possible to list all, but I will be in the subsequent update to complement, and will involve principle and source level analysis. Thank you for watching!!
Is everyone still ok? If you like, move your hands to show 💗, point a concern!! Thanks for your support!
Welcome to pay attention to the public number [Ccww technology blog], original technical articles launched at the first time