preface

If you are still wondering what a thread is and what a process is, please Google it first, as these concepts are outside the scope of this article.

The basic concept

Using multithreading has only one purpose, which is to make better use of CPU resources, because all multithreaded code can be implemented using a single thread. This is only half true, because the code that reflects “multiple actors” should at least be given a thread for each character, otherwise it is impossible to simulate the real world, and certainly impossible to implement with a single thread: for example, the most common “producer, consumer model”.

Many people are confused about some of these concepts, such as synchronization, concurrency, etc. Let’s set up a data dictionary to avoid misunderstanding.

  • Multithreading: When the program (a process) runs with more than one thread

  • Parallelism and concurrency:

  • Parallelism: The simultaneous execution of a piece of processing logic by multiple CPU instances or by multiple machines is truly simultaneous.

  • Concurrency: Through the CPU scheduling algorithm, the user appears to be executing simultaneously, but is not really simultaneous from the CPU operation level. Concurrency often has a common resource in the scene, so there is a bottleneck for this common resource, we will use TPS or QPS to reflect the processing capacity of the system.

  • Thread safety: Often used to describe a piece of code. In the case of concurrency, the code is used by multiple threads, the order in which the threads are scheduled does not affect any results. This time to use multithreading, we only need to pay attention to the system’s memory, CPU is not enough. Conversely, thread insecurity means that the order in which threads are scheduled affects the final result, such as a transaction-free transfer code:
void transferMoney(User from, User to, float amount){
    to.setMoney(to.getBalance() + amount);
    from.setMoney(from.getBalance() - amount);
}
Copy the code

  • Synchronization: Synchronization in Java refers to the use of artificial control and scheduling to ensure that multi-threaded access to shared resources becomes thread-safe to ensure accurate results. Simply add the @synchronized keyword in the code above. A good program is one that improves performance while ensuring accurate results. Thread safety takes precedence over performance.

All right, let’s get started. I’m going to summarize what’s involved with multithreading in several parts:

  1. Tie the horse: The state of the thread

  2. Internal mind method: every object has a method

  3. Taizu Changquan: Basic thread class

  4. Nine Yin true classics: advanced multithreaded control class

Tie the horse: The state of the thread

It’s worth noting the difference between “Blocked” and “Waiting” :

  • A thread in the Running state may encounter Blocked during the process of Running. Synchronized is added to the thread to make it enter the lock Blocked pool, and the lock is released to enter the Runnable state. Blocked, according to the JDK source code comments, refers to a wait for monitor (see the figure below) that the thread is in the wait area.

  • The thread may encounter Waiting in the process of Running. It can actively call Object. wait or sleep, or join (sleep is called inside the join, so it can be regarded as a kind of sleep) to enter. Waiting is waiting for another thread to complete an operation, such as join waiting for another thread to complete, object.wait() waiting for object.notify().

Waiting and Blocked states are a bit confusing. My personal understanding is: Blocked is actually a wait for monitor, but it is different from Waiting. For example, three threads enter the synchronized block, and two of them call Object.wait () and enter Waiting state. The third calls Object.Notifyall (), at which point one of the first two threads is moved to Runnable and the other to Blocked.

From the monitor structure diagram below: Each Monitor can only be owned by one Thread at a certain time, which is an “Active Thread”, while other threads are all “Waiting threads”, Waiting in two queues “Entry Set” and “Wait Set” respectively. The state of the Waiting thread in “Entry Set” is Blocked. According to jStack dump, it is “Waiting for monitor Entry”, while the state of the Waiting thread in “Wait Set” is Waiting. Jstack dump is “in object.wait ()”.

In addition, threads in the runnable state are the threads that are being scheduled, and the order in which they are scheduled is not necessarily the same. The yield method in the Thread class can turn a running Thread into a runnable.

Internal mind method: every object has a method

Synchronized, wait, and notify are synchronization tools available to any object. Let’s get to know them first

They are manual thread scheduling tools applied to synchronization problems. To get to the bottom of it, start with the concept of Monitor, where every object in Java has a monitor that monitors the reentrant of concurrent code. The monitor does not work in non-multithreaded coding, and does not work in synchronized range.

Wait /notify must exist in a synchronized block. Also, all three keywords are for the same monitor (the monitor of an object). This means that after wait, other threads can enter the synchronized block to execute.

When a right to use the code does not hold a monitor (as shown in figure 5 in the state, that is, from the synchronized block) to wait or notify, throws Java. Lang. IllegalMonitorStateException. This includes calling wait/notify of another object in a synchronized block, because different objects have different monitors and can throw this exception as well.

More usage:

  • Synchronized used alone:

  • Code block: As follows, in a multithreaded environment, methods in a synchronized block get the monitor of the lock instance, and if the instance is the same, only one thread can execute the block

public class Thread1 implements Runnable { Object lock; public void run() { synchronized(lock){ .. do something } } }Copy the code

  • Direct to methods: this is equivalent to using a lock in the above code to lock the monitor of class Thread1. Further, if you are modifying a static method, lock all instances of the class.
public class Thread1 implements Runnable { public synchronized void run() { .. do something } }Copy the code

  • Synchronized, Wait, and Notify: typical scenario producer-consumer problems
Public synchronized void produce() {if(this.product >= MAX_PRODUCT) {try {wait(); System.out.println(" product is full, please try again later "); } catch(InterruptedException e) { e.printStackTrace(); } return; } this.product++; System.out.println(" this. Product + "); notifyAll(); Public synchronized void consume() {if(this. Product <= MIN_PRODUCT) {try {public synchronized void consume() {if(this. Product <= MIN_PRODUCT) {try { wait(); System.out.println(" out of stock, fetch later "); } catch (InterruptedException e) { e.printStackTrace(); } return; } system.out.println (" this. Product + "); this.product--; notifyAll(); // Notify the waiting producer that the product can be produced}Copy the code

Volatile Multithreaded memory models: Main memory, working memory. While processing data, threads load values from main memory to the local stack, and then save them back. Each operation on this variable fires a load and save.

Variables used for multiple threads that are not volatile or final have the potential to produce unpredictable results (another thread changes the value, but a thread later sees the original value). In fact, the same property of the same instance has only one copy of itself. But multiple threads cache values, and volatile essentially means not caching them. Using volatile in thread-safe situations compromises performance.

Taizu Changquan: Basic thread class

The basic Thread class refers to the Thread class, the Runnable interface, and the Callable interface. The Thread class implements the Runnable interface, and starts a Thread method:

MyThread my = new MyThread(); my.start();Copy the code

Thread related methods:

// The current thread can transfer CPU control, Thread.sleep() public static thread.sleep () public static thread.sleep () // Calling other.join() in a thread will wait for other to complete before continuing the thread. Public join() public interrupte()Copy the code

About interrupts: It does not interrupt a running thread as the stop method does. From time to time, the thread checks the interrupt flag bit to determine whether the thread should be interrupted (if the interrupt flag value is true). Terminals affect only wait, sleep, and Join states. The interrupted thread throws InterruptedException. Thread.interrupted() checks whether the current Thread has been interrupted, returning Boolean synchronized cannot be interrupted while obtaining a lock.

Interrupt is a state! The interrupt() method simply sets this state to true. So a properly running program will not terminate without detecting state, whereas blocking methods such as WAIT will check and throw exceptions. If you add while(! Thread.interrupted()) can also leave the code body after interruption

Thread best practice: When writing, it is best to set the Thread name thread. name and set the Thread group ThreadGroup for easy management. In the event of a problem, the print thread stack (Jstack-PID) can see at a glance which thread is causing the problem and what the thread is doing.

How do I get exceptions in a thread

Runnable is similar to Thread

Callable Future: a type of concurrent mode, which can be in two forms: non-blocking and blocking, isDone and GET respectively. The Future object is used to store the return value and state of the thread

ExecutorService e = Executors.newFixedThreadPool(3); The submit method has multiple parameter versions and supports callable as well as runnable interface types. Future future = e.submit(new myCallable()); Future.isdone () //return true,false No blocking Future.get () //return returns a value and blocks until the thread finishes runningCopy the code

Nine Yin true classics: advanced multithreaded control class

Java1.5 provides a very efficient and practical multithreaded package :java.util.concurrent, which provides a number of advanced tools to help developers write efficient, easy to maintain, structured Java multithreaded programs.

1. Ref;

Use: Save thread independent variables. When using ThreadLocal to maintain variables, ThreadLocal provides a separate copy of the variable for each Thread that uses the variable, so each Thread can independently change its own copy without affecting the corresponding copy of other threads. It is used for user login control, such as recording session information.

Implementation: Each Thread holds a variable of type TreadLocalMap. This class is a lightweight Map that provides the same functions as a Map, except that buckets contain entries instead of a linked list of entries. Function is still a map. Take itself as the key and the target as value. The main methods are get() and set(T a). After set, a threadLocal -> a is maintained in the map, and a is returned when GET. ThreadLocal is a special container.

2. Atomic classes (AtomicInteger, AtomicBoolean…)

Using an Atomic Wrapper class such as atomicInteger, or using its own guarantee atom operations, is equivalent to synchronized

/ / the return value to a Boolean AtomicInteger.com pareAndSet (int expect, int the update)Copy the code

This method can be used to implement optimistic locking, considering the scenario originally mentioned in this article: A pays B $10, A deducts $10, and B adds $10. At this time, C gives B2 yuan, but B’s plus ten yuan code is about:

if(b.value.compareAndSet(old, value)){
   return ;
}else{
   //try again
   // if that fails, rollback and log
}
Copy the code

AtomicReference For AtomicReference, maybe the object will lose its property, so oldObject == current, but oldobject.getPropertya! = current getPropertyA. That’s where your AtomicStampedReference comes in. This is also a very common idea, which is to add the version number

3. The Lock classes

Lock: in the java.util.concurrent package. There are three implementations:

  • ReentrantLock

  • ReentrantReadWriteLock.ReadLock

  • ReentrantReadWriteLock.WriteLock

The main purpose is the same as synchronized, both are to solve the synchronization problem, deal with the resource dispute and produced technology. The function is similar but there are some differences. The differences are as follows:

  1. Lock is more flexible, you can freely define the sequence of unlocking multiple locks (synchronized is added first and solved later)

  2. Offers a variety of locking schemes, including Lock blocking, trylock non-blocking, lockInterruptible, and trylock with timeout.

  3. Essentially the same as synchronized

  4. The greater the ability, the greater the responsibility, must control the lock and unlock, otherwise it will lead to disaster.

  5. And Condition class.

  6. Higher performance, for example:

The ReentrantLock is reentrantable in that the thread holding the lock can hold it for an equal number of times before the lock is actually released. 1. Create an instance

static ReentrantLock r=new ReentrantLock();
Copy the code

2. Lock

R.l ock () or r.l ockInterruptibly ();Copy the code

Here, too, the latter can be interrupted. After thread A locks, thread B (lockInterruptibly) will stop blocking, give up fighting for resources, and enter the catch block after calling B.interrupt (). (If the latter is used, you must throw Interruptable Exception or catch) 3. Release the lock

r.unlock()
Copy the code

Must do! What must be done? Put it in finally. To prevent abnormal out of the normal process, resulting in disaster. As an added tidbit, finally can be trusted: after testing, statement execution ina finally block can be guaranteed even when an OutofMemoryError occurs.

ReentrantReadWriteLock

Reentrant read-write lock (an implementation of read-write lock)

ReentrantReadWriteLock Lock = new ReentrantReadWriteLock() ReadLock r = lock. ReadLock (); WriteLock w = lock.writeLock();Copy the code

They both have lock,unlock methods. Write, write read mutually exclusive; Read not mutually exclusive. Efficient thread-safe code that can implement concurrent reads

4. The container classes

Here are two common ones:

  • BlockingQueue

  • ConcurrentHashMap

BlockingQueue blocks the queue. This class is an important class under the java.util.Concurrent package. As you can see from the Queue, this Queue is a one-way Queue that can add elements at the head of the Queue and remove or retrieve elements at the end. Similar to a pipeline, it is particularly suitable for some applications of fifO. The common queue interface mainly implements PriorityQueue (PriorityQueue), can be interested in research

BlockingQueue adds multi-threaded collaboration to queues:

In addition to the traditional queue functionality (the two columns on the left of the table), there are also blocking interfaces put and Take, and blocking interfaces offer and poll with timeout functionality. Put blocks when the queue is full until it is woken up when there is space; Take blocks when the queue is empty and is not woken up until something is taken. It is especially useful for producer-consumer models and is a miracle.

Common blocking queues are:

ArrayListBlockingQueue

LinkedListBlockingQueue

DelayQueue

SynchronousQueue

ConcurrentHashMap Efficient thread-safe hash map. Compare hashTable, concurrentHashMap, and HashMap

5. Management class

The management class concept is more general and is used to manage threads. It is not multithreaded per se, but provides mechanisms to do some encapsulation using the tools described above. Notable management classes to learn about: ThreadPoolExecutor and ThreadMXBean, the system-level management class from the JMX framework

ThreadPoolExecutor, if not familiar with this class, should know about the previously mentioned ExecutorService, which makes it very convenient to start your own thread pool:

ExecutorService e = Executors.newCachedThreadPool(); ExecutorService e = Executors.newSingleThreadExecutor(); ExecutorService e = Executors.newFixedThreadPool(3); // The first type is variable size thread pool, which allocates threads according to the number of tasks, the second type is single thread pool, which is equivalent to FixedThreadPool(1) // the third type is fixed size thread pool. // Then run e.execute(new MyRunnableImpl());Copy the code

This class is internally implemented through ThreadPoolExecutor, and it helps to understand thread pool management. They are essentially implementations of the ThreadPoolExecutor class.

CorePoolSize: The initial and minimum number of threads in the pool, which is maintained even in idle state. MaximumPoolSize: The maximum number of threads that never grow beyond this value. KeepAliveTime: When the number of threads in the pool is higher than corePoolSize, how much time passes before the excess idle threads are collected. In wait state unit: TimeUnit. You can use an instance of TimeUnit, for example, timeunit.milliseconds workQueue: indicates the waiting place for a Runnable task. This parameter affects scheduling policies, such as whether it is fair or not. Starving threadFactory: a threadFactory class with default implementation, which implements the threadFactory interface and passes it in as a parameter if needed.