Java multithreading knowledge summary


Processes, threads, and programs

  • Program
    • A set of instructions written in a language that does not complete a specific task, that is, a static code, a static object
  • Process
    • An execution process of a program, or a running program, is a dynamic process with its own generation, existence, and extinction
      • life cycle
      • Running QQ, running MP3
      • The process cannot control its own resource allocation, and the system dynamically allocates resources to it when it runs
  • Thread thread
    • An execution path within a program
    • If a process has multiple threads in parallel at the same time, it supports multiple threads
    • Thread is the unit of scheduling and execution. Each thread has an independent stack and program counter. The switching overhead of thread is small
      • Multiple threads share the method area and heap in a process
      • This means that multiple threads can operate the data in the heap and method area at the same time, resulting in potential security risks

Single core and multi-core CPU s

  • Single core CPU
    • False multithreading, because only one thread can be executed in a time unit
    • Therefore, the CPU is always suspending some programs; Because the CPU time unit (switching speed) is very short, I can't feel it
    • Multi core CPU can give better play to the efficiency of multithreading
    • main() main thread, gc() garbage collection thread, exception handling thread – a java program has at least 3 threads
  • parallel
    • Multiple CPU s execute multiple tasks at the same time, such as multiple people doing different things; It's like multiple lanes
  • Concurrent
    • One CPU uses time slice to execute multiple tasks at the same time; Just like a gas station, it always hangs other customers on the road every time it charges; It's just that the pending switching speed is fast, and you can't feel it


  • advantage

    • For a single core CPU, if only a single thread is used, the running efficiency is faster, because in this way, the CPU does not need to be switched; On the contrary, when multithreading, the CPU needs to switch constantly, but it is slower
    • Improve the corresponding of computer programs, especially graphical Pages - a thread for user input and a thread for background calculation; This can improve the user experience, because a user may do more than one thing at a time, unlike dos, which only does one thing at a time
    • Improving cpu utilization of computer system
    • Improve the program structure, divide the long and complex process into multiple threads and run independently, which is conducive to understanding and modification
  • appropriate

    • The program needs to perform two or more tasks at the same time
    • The program realizes some tasks that need to wait, such as user input, network search, file reading and writing
      • For example, when the thread is running for file read-write operation, using synchronous IO will waste resources and may cause jamming, such as sliding of meituan takeout
  • When you need some programs running in the background

Classification of threads

  • Threads in Java are divided into two categories: one is daemon thread and the other is user thread.
    • They are the same in almost every way, and the only difference is to judge when the JVM leaves
    • Daemon threads are used to serve user threads, and are invoked before the start() method.
      • thread.setDaemon(true) can turn a user thread into a daemon thread.
    • Java garbage collection is a typical daemon thread.
    • If there are daemon threads in the JVM, the current JVM will exit.
    • Image understanding: the rabbit dies and the dog cooks, and the birds hide

Thread creation and startup

  • If the execution process of a program can be drawn with a line, it is single thread

Extends java.lang.Thread

  • Code

    • public class ThreadTestExtend2 {
      	public static void main(String[] args) {
      		MyStringTest t = new MyStringTest();
      class MyStringTest extends Thread {
      	public void run() {
      • Create Thread subclass
      • Override run(), which contains what the thread does
      • Instantiate the subclass
      • Call start in Thread class
  • problem

    • cannot be called directly to start a thread

      • The above method is equivalent to calling common method in main.
    • start() cannot be called twice for the same thread object

      • if (threadStatus != 0)
                    throw new IllegalThreadStateException();
        //As shown above, a thread pair can be start ed at most once; Otherwise, an IllegalThreadStateException is obtained
      • But you can recreate a thread object

    • Anonymous Class

      • Creating an anonymous subclass of Thread can realize fast write multithreading

implements Runnable :happy:

  • Code

    • public class ThreadTestImplement {
      	public static void main(String[] args) {
      		new Thread(new MThread()).start();
      class MThread implements Runnable{public void run() {...}}
      • Create a class that implements the Runnable interface

      • The implementation class implements the run() abstract method

      • Instantiate the object of the class

      • Pass the Runnable object as a parameter into the constructor of the Thread class, create the Thread class object and run start()

        • public Thread(Runnable target){this(null, target, "Thread-" + nextThreadNum(), 0);}
      • The above method can succeed because the private Runnbale target will be assigned a value when using the above method. In fact, run() calls the run method in the target; This is why rewriting is not equally successful

  • This method is suggested

    • Implementation is a relationship that can be done, while inheritance is a; This operation is more intuitive
    • The implementation method naturally realizes the sharing of data without using the static keyword
    • In addition, this method can also realize multi inheritance in a sense
    • They are all trying to modify the run method


  • Since JDK 5.0

  • Callable is more powerful than Runnable

    • Compared with the run() method, you can have a return value
    • Method can throw an exception
      • Once an exception is thrown, the result is that the current thread terminates; However, you can't directly catch the exception thrown by that thread. You can get the ExecutionException at most. The getMessage inside can let you know what the exception is thrown
    • Return values that support generics
    • With the FutureTask class, you can get the returned results
  • Code

    • public class CallableSoldTicket {
      	public static void main(String[] args) {
      		Window w = new Window();//Instantiate implementation class
      		FutureTask<Integer> f1 = new FutureTask<Integer>(w); //Create FutureTask object
      		Thread w1 = new Thread(f1);//Create a new thread
      		int o1 = 0;
      		try {o1 = f1.get();}//Get return value
      		catch (InterruptedException e) {e.printStackTrace();} 
      		catch (ExecutionException e) {e.printStackTrace();}
              //If an exception occurs, catch the ExecutionException; The inner information contains the exception thrown in the implementation class
      		System.out.println(o1); //Output return value
      class Window implements Callable<Integer> {//Create an implementation class for the Callable interface
      	private int ticket = 100;
      	private volatile boolean flag = true;
      	private ReentrantLock lock = new ReentrantLock();
      	public Integer call() throws Exception {
      		long start = System.currentTimeMillis();
      		while (flag) {
      			try {lock.lock();
      				if (ticket > 0) {
      					System.out.println(String.format("window: %s,Sell tickets: %d", Thread.currentThread().getName(), ticket--));
      					try {Thread.sleep(10);} 
      					catch (InterruptedException e) {e.printStackTrace();}
      				} else {flag = false;}
      			} finally {	lock.unlock();}
      		long end = System.currentTimeMillis();
      		return Integer.valueOf((int) (end - start));
      • Create an implementation class for the Callable interface
      • Implement the call method to declare the operations that this thread needs to perform. In this method, return values and throw exceptions are allowed
      • Create a Callable interface implementation class object and pass it as a parameter to the constructor of FutureTask class. Start the constructor that passes FutureTask class as a parameter to Thread() and run it
      • Finally, futureTask.get gets the return value


  • background

    • Creating and destroying threads per se means significant performance overhead
  • thinking

    • Create multiple threads in advance, put them into the thread pool, obtain them directly when using them, and put them back into the pool after use. It can avoid frequent creation, destruction and reuse
  • benefit

    • Improved response time (reduced time to create new threads)
    • Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
    • Easy thread management
      • corePoolSize: the size of the core pool
      • maximumPoolSize: maximum number of threads
      • keepAliveTime: how long does the thread last when there is no task, and then it will terminate
  • example

    • JDK 5.0 provides thread pool related API s: ExecutorService and Executors
    • ExecutorService
      • Real thread pool interface. Common subclass ThreadPoolExecutor
      • void execute(Runnable command)
        • Execute a task / command without a return value. It is generally used to execute Runnable
      • <T> Future < T > submit (Callable < T > task) executes a task and has a return value. Generally, it executes Callable again
      • void shutdown() closes the connection pool
    • Executors tool class and thread pool factory class are used to create and return different types of thread pools
      • Executors.newCachedThreadPool() creates a thread pool where new threads can be created as needed
      • Executors.newFixedThreadPool(n) creates a thread pool with a fixed number of reusable threads
      • Executors.newSingleThreadExecutor() creates a thread pool with only one thread
      • Executors.newScheduledThreadPool(n) creates a thread pool that can be scheduled to run after a given delay
        Execute commands or periodically
  • Code

    • public class ThreadPoolTest2 {
      	public static void main(String[] args) {
      		ExecutorService service = Executors.newFixedThreadPool(4);
      		FutureTask f = new FutureTask<Integer>(new myThread());
      		service.submit(new FutureTask<Integer>(new myThread()));
      		service.submit(new FutureTask<Integer>(new myThread()));
      		try {
      		} catch (InterruptedException e) {
      		} catch (ExecutionException e) {
      class myThread implements Callable<Integer> {
      	public Integer call() throws Exception {
      		int sleepTime = new Random().nextInt(100);
      		String whoAmI = Thread.currentThread().getName();
      		System.out.println(whoAmI + ":" + sleepTime);
      		try {
      		} catch (InterruptedException e) {
      		return sleepTime;
      • Create a thread pool using the static methods provided in the Executors factory class
      • Use the returned thread pool object, submit() to add a Callable thread, and execute() to add a Runnable or Callable thread
      • Close thread pool

Common thread methods

  • void start()

    • Start the thread and execute the run() method of the object
  • run()

    • Action to perform when calling a thread

    • This method needs to be overridden, otherwise the default method is called

    • public void run() {
          if (target != null) {
  • <ThreadInstance>.getName()

    • The name of the current thread

    • Reasons for automatic naming

    •     public Thread() {
              this(null, null, "Thread-" + nextThreadNum(), 0);
         	//The above explains the Thread-0,1,2... names
          public Thread(ThreadGroup group, Runnable target, String name,
                        long stackSize) {
              this(group, target, name, stackSize, null, true);
      	//stackSize is a suggestion to JVM virtual machine. It may improve stack depth and prevent StackOverflowError if set high; or allowing more thread run concurrently and prevent OutOfMemoryError. However, no guarantee are here for this behavior, and highly dependent on Platform & JRE Implementation
  • Thread.currentThread()

    • Static method to get the current thread
  • <ThreadInstance>.setName(String name)

    • Instance method to modify the name of the current thread

    • Modify the name before calling the start() method, otherwise the thread name may change suddenly during / after running

    • public Thread(String name) {
          this(null, null, name, 0);
      //This method allows you to specify the name directly when creating your own thread
  • Thread.yield()

    • Static method
    • Pause the thread currently executing and give the execution opportunity to the thread with the same or higher priority
    • If there are no threads with the same priority in the queue, this method is ignored
  • Thread.join()

    • If B.join() is used in thread A, A starts executing until B finishes executing

    • InterruptedException will be thrown

    • public final synchronized void join(final long millis)//Override method join() == join(0)
          throws InterruptedException {
          if (millis > 0) { //If the waiting time is greater than 0, wait as long as the added thread is not dead and the execution time is not greater than delay; The while method guarantees enough time
              if (isAlive()) {
                  final long startTime = System.nanoTime();
                  long delay = millis;
                  do {
                  } while (isAlive() && (delay = millis - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
          } else if (millis == 0) {
              while (isAlive()) {//If the wait time is equal to zero, wait indefinitely until the thread completes execution
          } else {
              throw new IllegalArgumentException("timeout value is negative");
    • example

    • Thread even = new Thread() {
          public void run() {
              for (int i = 0; i <= 10; i += 2) {
                  System.out.println(i + "\t:\t" + Thread.currentThread().getName());
      for (int i = 1; i < 5; i++) {
          System.out.println(i + "\t:\t" + Thread.currentThread().getName());
          if (i == 2) {
              try {
              } catch (InterruptedException e) {
      //1	:	main
      //two 	:	 main / / here, the event. Join () method is called
      //0	:	Thread-0
      //2	:	Thread-0
      //4	:	Thread-0
      //6	:	Thread-0
      //8	:	Thread-0
      //10:	Thread-0
      //3	:	main
      //4	:	main
  • static void sleep(long millis)

    • Let the active thread give up control over CPU resources within a specified period of time, and other threads execute immediately regardless of priority
    • throw InterruptedException, if other threads disturb this thread
  • isAlive() determines whether the thread is active

  • @deprecated, stop() forces the thread to end

    • Because ThreadDeathError will be thrown when the stop() method is called; This error will not generate any prompt, and will kill the thread and release all locks it holds; Therefore, this may cause multithreading safety problems
    • It is recommended to use the volatile keyword as the flag, and let the object periodically check the flag to determine whether it should continue to run
    • Or use the interrupt() method
  • java.lang.object

    • wait()
      • Similarities and differences between and sleep()
        • Once the method is executed, the current thread can enter the blocking state
        • The positions of the two method declarations are different. sleep() is declared in the Thread class and wait() is declared in the Object class
        • The requirements of the two methods are different, sleep() can be invoked in any scenario, but wait() must be invoked in the synchronous code block.
        • The lock is not released after the sleep() call, but will be released after the wait() call
    • notify()
    • notifyAll()

Thread scheduling

  • Time slice

    • Each process runs in turn

    • [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-0qhpsqwu-1633987865100) (note. assets/image-20211010223606744.png)]

  • Preemptive

    • High priority threads preempt CPU
  • Java scheduling method

    • Thread FIFO of the same priority serves first come, first serve, and uses time slice strategy
    • High priority threads use the preemptive strategy of priority scheduling
  • constant

    • MAX_PRIORITY = 10
    • MIN_PRIORITY = 1
  • method

    • public int getPriority() returns priority
    • public setPriority(int newPriority)
      • The priority must be between [1,10], otherwise the IllegalArgumentException
      • This priority must be below the maximum priority of the thread group, otherwise it will be automatically changed to the maximum priority of the thread group
    • The level of priority does not necessarily determine that a thread must execute to the
    • We can only say that threads with high priority are more likely to be executed; This is only a probability, not a guarantee

life cycle

  • The process from birth to death of an object is called the declaration cycle of a thread
  • The state of the thread, which is defined in the enumeration class of Thread.State
    • NEW is a thread that has been created and has not yet started
    • RUNNABLE can be executed, and start() has been called; Wait for the virtual machine to allocate resources to it
    • BLOCKED waits for the thread lock to enter a synchronized method block
    • WAITING waits for other threads to execute Object.notify() / Object.notifyAll(), and calls wait() / join to enter this state
    • TIMED_WAITING is waiting for a certain period of time because sleep()/wait(long)/join(long) is used
    • TERMINATED if a thread has finished executing the run() method, it will die
  • It may be simplified to five cases

Thread communication

  • wait() causes the current thread to suspend and abandon the CPU, synchronize resources and wait, so that other threads can access and modify shared resources; Wait for other threads to call the notify() or notifyAll() method to wake up. After waking up, wait until you regain ownership of the monitor before proceeding
  • notify() wakes up the thread with the highest priority among the threads queued for synchronization resources, that is, the thread waiting()
  • notifyAll() wakes up all threads queued for resources
  • matters needing attention
    • These three methods can only be used in the synchronized method or synchronized code block, otherwise an exception will be reported
    • The caller of these three methods must be the synchronization monitor, otherwise there will be a problem
      • java.lang.IllegalMonitorStateException
      • Any object can be used as a synchronized synchronization lock
      • These three methods can only be declared in the Object class

Synchronous implementation


  • Multiple threads read and write shared data at the same time, which may cause thread safety problems; Therefore, if there is a single thread or no shared data, there will be no thread safety problem
  • This is because multiple threads read the internal cache of threads, resulting in incomplete visibility and transparency of modifications between threads; Each thread may not be able to complete the execution of the code to be synchronized in the time slice allocated by each CPU (that is, it is suspended when half of the execution is not completed); The order in which each thread executes is not necessarily the same as the order written in the code
  • as
    • When a bank withdraws money, two people withdraw money from an account (shared data) at the same time
    • A. if the judgment is passed, suspend; B. if the judgment is passed, suspend; Finally, a and B withdraw two pieces of money from the account book at the same time, and the two pieces of money may be greater than the remaining balance
    • Although the probability of the above situation is very small, we still need to solve this problem


  • When a thread operates on shared data, other data cannot participate. Other threads cannot continue to operate ticket until the thread completes its operation - blocking other threads


Synchronous code block

  • synchronized(Object Monitor){...}
  • The code that operates shared data is the code that needs to be synchronized

  • Shared data, that is, variables operated by multiple threads, such as instance variables and static variables of classes

  • Sync monitor < = > lock

    • The object of any class can act as a lock, even a new Object()
    • Because there is only one object, all threads can only share the same lock; Otherwise, the key part of multithreading cannot be transformed into single thread, and the thread safety problem will not be solved
      • If all threads have a lock, the problem is not solved

Synchronization method

  • Synchronization monitor

    • Non static method, synchronous monitor is equivalent to

      • synchronized(this){...}
    • Therefore, when dealing with multithreading implemented by extensions, we must change the method to static method - there are multiple objects; Otherwise, each thread will hold a lock, resulting in thread safety problems

      • Static method, the synchronization monitor will be Window4.class (the current class itself)
      • Because the class is unique, it is fixed
    • You need to consider whether multiple threads share the same lock!

Synchronous lazy

  • class Bank {
    	private Bank() {}
    	private volatile static Bank instance = null;
    	public static Bank getInstance() {
    		if (instance == null) {
    			synchronized (Bank.class) {
    				if (instance == null) {	instance = new Bank();}
    		return instance;// There is no structural operation on shared data
  • If the first thread enters and judges, and after the object is created, you can directly return the instance to all other threads without asking them to enter the synchronization method for judgment

  • volatile can improve efficiency, so that the instance can be quickly written to main memory at the moment of operating data

    • However, using volatile alone is not enough, because instance = new Bank() is not an atomic operation, and multiple problems may occur when creating a bank


  • Different threads occupy the synchronization resources needed by the other party and do not give up. They are waiting for the other party to give up the synchronization resources they need, forming a thread deadlock

    • For example, two people eat, but only one pair of chopsticks; Neither of them took a chopstick and waited for the other party to give up chopsticks, so there was the problem of no one eating.
  • After a deadlock occurs, there will be no exception or prompt, but all threads are blocked and cannot continue

  • resolvent

    • Special algorithms and principles
    • Minimize the definition of synchronization resources
    • Try to avoid nested synchronization
  • It should be noted that deadlocks can occur without being discovered - this requires careful coding!

  • example

    • package;
      class A {
      	public synchronized void foo(B b) {
      		System.out.println("Current thread name: " + Thread.currentThread().getName()
      				+ " Entered A Instance foo method"); // ①
      		try {
      		} catch (InterruptedException ex) {
      		System.out.println("Current thread name: " + Thread.currentThread().getName()
      				+ " Attempt to call B Instance last method"); // ③
      	public synchronized void last() {
      		System.out.println("Entered A Class last Method internal");
      class B {
      	public synchronized void bar(A a) {
      		System.out.println("Current thread name: " + Thread.currentThread().getName()
      				+ " Entered B Instance bar method"); // ②
      		try {
      		} catch (InterruptedException ex) {
      		System.out.println("Current thread name: " + Thread.currentThread().getName()
      				+ " Attempt to call A Instance last method"); // ④
      	public synchronized void last() {
      		System.out.println("Entered B Class last Method internal");
      public class DeadLock implements Runnable {
      	A a = new A();
      	B b = new B();
      	public void init() {
      		Thread.currentThread().setName("Main thread");
      		// Call foo method of a object;
      		System.out.println("After entering the main thread");
      	public void run() {
      		Thread.currentThread().setName("Secondary thread");
      		// Call the bar method of the b object;
      		System.out.println("After entering the secondary thread");
      	public static void main(String[] args) {
      		DeadLock dl = new DeadLock();
      		new Thread(dl).start();
      • We found that if the thread main wants to run, it must obtain two synchronization monitors a and B at the same time
      • If a thread or a sub thread wants to run, it must wait for two synchronization monitors b,a
      • So the deadlock happened


  • Since JDK 5.0

  • java shows that a synchronization lock object is defined to achieve synchronization

  • The java.util.concurrent.locks.Lock interface is a tool for controlling access to shared resources of multiple thread heaps; A typical implementation is

    • ReentrantLock, you can display the lock and release the lock
  • step

    • Create a unique ReentrantLock object

    • try{
      	...//The code that need to synchronized, here
      • Use the above format to ensure unlocking, even if there is return or other possible exceptions
      • lock must release the monitor manually, and synchronized can automatically release the synchronization monitor after execution
  • lock is recommended for more flexibility

advantages and disadvantages

  • The above method solves the thread safety problem
  • After synchronization, only one thread can participate and other threads wait; It becomes an inefficient single thread – a limitation
    • Therefore, only check the code to be synchronized, not more or less
    • Multiple choices cause performance problems
    • Synchronization failure caused by less selection


  • volatile

    • Volatile can be regarded as lightweight Synchronized, which only ensures the visibility of shared variables (a certain order).
    • After thread A modifies the shared variable modified by volatile, thread B can read the correct value.
      • volatile block
        • Internal thread caching and reordering, that is, directly modifying memory
        • Read directly from memory and modify directly from memory
      • synchronized and lock also guarantee visibility because they guarantee
        • At the same time, only one thread obtains the lock and executes the code
        • Changes to variables are flushed into memory before the lock is released
  • Three concepts

    • visibility

      • Visibility refers to the visibility between threads. The modified state of one thread is visible to another thread. That is, the result of thread modification can be seen by another thread immediately.
    • Atomicity

      • That is, one or more operations are either fully executed and the execution process will not be interrupted by any factor, or they will not be executed

      • The Java memory model only guarantees that basic reads and assignments are atomic operations

      • public class VolatileTest {
        	public static void main(String[] args) {
        		final Test test = new Test();
        		for (int i = 0; i < 10; i++) {
        			new Thread() {
        				public void run() {
        					for (int j = 0; j < 1000; j++)
        						test.increase();//Because volatile does not guarantee atomicity, inc + + is not guaranteed to be calculated and written to memory in the same thread. Therefore, suppose the following situations: A reads inc and gets 10, and then interrupts; B reads inc and gets 10, because A has not written to memory, and calculates and writes inc = 11; A gets resources and gets inc = 11; therefore, the result of inc is always < 10000
        		while(Thread.activeCount()>1) {
        			//Ensure that all previous threads are executed            
        class Test {
        	public volatile int inc = 0;
        	public void increase() {
    • Order

      • The processor may modify the execution order of the code to improve efficiency
      • The operation results are consistent, but the execution sequence is not consistent
      • volatile is equivalent to memory fence, which ensures
        • During the operation of volatile, the changes of the previous operations must have been made, and the results have been visible to the subsequent operations; the subsequent operations must not have been carried out;
        • You cannot execute a statement after a read or write operation on a volatile variable, nor can you execute a statement after a volatile variable before it.
        • However, it is not guaranteed that the previous operation sequence is correct and the subsequent operation sequence is correct
  • Usage scenario

    • Writes to variables do not depend on the current value – non self increasing, self decreasing
    • This variable is not included in an invariant with other variables – the assignment does not depend on other variables
    • It's good to represent a concurrent flag, etc., because it's faster

AtomicInteger, AtomicLong, etc

  • These ensure the atomicity of self increasing and self decreasing budgets. They are special classes and are worth using

Tags: Java

Posted on Mon, 11 Oct 2021 17:14:04 -0400 by Btown2