1. What is the difference between parallelism and concurrency?

Parallel: multiple tasks are executed simultaneously in unit time.

Concurrency: Multiple tasks are executing at the same time.

For example, concurrency is a person eating three apples at the same time; Parallelism is three people eating three apples at once.

How many ways are there to create a thread?

There are four creation methods:

1) Inherit the Thread class as follows:

  • Define a class and make it inheritThreadClass;
  • rewriterun()Method,run()The method body of a method is the task to be performed by the thread;
  • Create a thread instance and call the start() method to start the thread.
  • The code is as follows:
package thread;
public class MyThread extends Thread {
 
 @Override
 public void run(a) {
     for (int i = 0; i < 100; i++) {
         System.out.println(getName() + "-"+ i); }}public static void main(String[] args) {
     for (int i = 0; i < 100; i++) {
         System.out.println(Thread.currentThread().getName() + "= = = = =" + i);
         if (i == 50) {
             new MyThread().start();
             newMyThread().start(); }}}}Copy the code

2) Implement the Runnable interface as follows:

  • defineRunnableInterface implementation class, rewriteRun()Method, which is also the execution body of the thread;
  • Create an instance object of the implementation class, and use this instance object as the target of Thread to create a Thread object, the Thread object is the real Thread object;
  • callstart()Method to start a thread.
  • The code is as follows:
package thread;
public class RunnableThreadTest implements Runnable {
 
 @Override
 public void run(a) {
     for (int i = 0; i < 100; i++) {
         System.out.println(Thread.currentThread().getName() + "--"+ i); }}public static void main(String[] args) {
     for (int i = 0; i < 100; i++) {
         System.out.println(Thread.currentThread().getName() + "= =" + i);
         if (i == 20) {
             RunnableThreadTest rtt = new RunnableThreadTest();
             new Thread(rtt, Thread 1 "").start();
             new Thread(rtt, Thread 2 "").start(); }}}}Copy the code

Implementing the Runnable interface is more flexible than inheriting Thread because you can implement multiple interfaces but only inherit one class.

3) To implement Callable interface, the steps are as follows:

  • createCallableInterface implementation class, rewritecall()Method, whichcall()Method will be the thread body and have a return value;
  • Creates an instance object of the implementation class usingFutureTaskClass to packagingCallableObject, whichFutureTaskObject encapsulates thisCallableThe object’scall()Method return value;
  • useFutureTaskObject as aThreadThe object’stargetCreate and start a new thread;
  • callFutureTaskThe object’sget()Method to get the return value after the child thread completes execution.
  • The code is as follows:
package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/** * Create an implementation class for the Callable interface */
public class CallableThreadTest implements Callable {
 /** * overrides the run method, which acts as the thread body and returns the value */
 @Override
 public Integer call(a) throws Exception {
     int i = 0;
     for (; i < 100; i++) {
         System.out.println(Thread.currentThread().getName() + "= = =" + i);
     }
     return i;
 }

 public static void main(String[] args) {
     // Create an instance object of the implementation class
     CallableThreadTest ctt = new CallableThreadTest();
     // Use the FutureTask class to wrap the Callable object, which encapsulates the return value of the call() method
     FutureTask<Integer> ft = new FutureTask<>(ctt);
     for (int i = 0; i < 100; i++) {
         System.out.println(Thread.currentThread().getName() + "= =" + i);
         if (i == 20) {
             // Create and start a Thread using the FutureTask object as the target parameter of the Thread object
             new Thread(ft, "Thread with return value").start(); }}try {
         // Call FutureTask's get() method to get the return value when the child thread finishes executing
         System.out.println("Return value of child thread:" + ft.get());
     } catch (InterruptedException e) {
         e.printStackTrace();
     } catch(ExecutionException e) { e.printStackTrace(); }}}Copy the code
3,RunnableThe interface andCallableWhat is the difference between interfaces

The return value of the run() method in the Runnable interface is void, which simply executes the code in the run() method;

The Call () method in the Callable interface, which returns a value, is a generic type that can be used in conjunction with Future and FutureTask to retrieve the result of asynchronous execution.

This is a very useful feature, because multithreading is fraught with uncertainty. Does a thread execute? How long did it take? Is the expected data already assigned when a thread executes? We don’t know. All we can do is wait for the multithreaded task to finish. Callable+Future/FutureTask can retrieve the result of multi-thread running. It can cancel the task if the waiting time is too long to obtain the required data.

What are the states of threads?

Java threads have five basic states:

Threadt = New MyThread(); threadt = New MyThread();

Runnable: A thread enters the ready state when t.start() of the thread object is called. If a thread is in the ready state, it is ready to be executed by the CPU, not immediately.

Running: When the CPU dispatches a thread in the ready state, the thread can actually execute, that is, enter the Running state.

Note: The ready state is the only entry to the running state, that is, in order for a thread to enter the running state to execute, it must first be in the ready state.

The process is Blocked when a thread in the running state temporarily relinquish access to the CPU and stops executing for some reason. Until it is ready again, it has no chance to be called into the running state by the CPU. According to different causes of blocking, blocking states can be divided into three types:

  • Wait to block: a thread in the running state executeswait()Method to make the thread enter the waiting blocking state;
  • Synchronous blocking: thread acquisitionsynchronizedWhen a synchronization lock fails (the lock may be occupied by another thread), it enters the synchronization blocking state.
  • Other blocking: of the calling threadsleep()orjoin()Or sentI/OWhen requested, the thread enters a blocked state. whensleep()When the status times out,join()Wait for the thread to terminate or time out, orI/OWhen the processing is complete, the thread goes back to the ready state.

Dead: a thread terminates its life cycle when it finishes executing or exits the run() method because of an exception.

5,wait()Methods andsleep()What’s the difference?
  • Wait () is an Object method, and sleep() is a Thread method.

  • Wait () releases the lock, sleep() does not release the lock; (Waiting is active and releases the lock; Asleep and forgot to release the lock,)

  • Sleep () is often used to pause a thread, while wait() is used for interthread communication.

  • If you call wait(), the thread will wake up only after notify() or notifyAll() of the same object is called by another thread, or if you wait(long timeout), the thread will wake up automatically. Execute sleep(long millis), the thread automatically wakes up when the time is up;

  • Both methods can cause the thread to pause execution.

6. Threadedyield()andjoin()The role of

1) Basic knowledge of Java thread scheduling:

① In a variety of threads, the JVM must implement a priority-based scheduler. This means that each thread in a Java program is assigned a priority, expressed as a defined positive integer. The priority can be changed by the developer, but the JVM does not change the priority either.

② Setting the priority is important because the convention between the JVM and the underlying operating system is that the operating system must select the high-priority Java thread to run. So let’s say Java implements a priority-based scheduler. This means that when a thread with a higher priority arrives, it is interrupted regardless of whether the lower priority thread is running. This convention is not always the case for the underlying operating system, which may sometimes choose to run a lower priority thread.

2) Thread priority

Understanding thread priorities is an important step in multithreaded learning, and in particular how the yield() method works:

① When the priority of a thread is not specified, all threads carry the common priority.

② The priority can be specified on a scale from 1 to 10. 10 is the highest priority, 1 is the lowest priority, and 5 is the common priority.

③ The thread with the highest priority is given priority in execution;

(4) The currently running thread may always have higher priority than the thread waiting for the opportunity to run in the thread pool;

⑤ It is up to the scheduler to decide which thread is executed;

⑥t.setPriority() is used to set the priority of the thread;

⑦ The priority of the thread should be set before the thread start() method is called.

We can use constants such as MIN_PRIORITY,MAX_PRIORITY, NORM_PRIORITY to set the priority.

3) Yield (

① This method belongs to the Thread class, and the yield translates to “yield”. So yield() is translated by many as thread concession, which means that when a thread uses this method, it gives up its CPU execution and lets itself or another thread run;

② This method changes the current thread from the running state to the ready state. The CPU selects execution from the thread that is in the ready state, that is, it is possible that this thread will be executed again, not necessarily that another thread will execute and this thread will not execute.

4) Join () method

The join() method adds the specified thread to the current thread and converts two alternating threads to execute sequentially. For example, if thread B calls thread A’s join() method, thread B will not continue executing until thread A finishes executing.

7. Threadednotify()andnofityAll()What’s the difference?

1) Two concepts: lock pool and wait pool

1) lock pool: Suppose thread A already owns the lock on an object (not A class), and other threads want to call A synchronized method or block of that object. Since these threads must acquire the lock on the object before entering the synchronized method or block of the object, the lock is currently owned by thread A. So these threads go into the lock pool for that object.

② Wait pool: If thread A calls wait() on an object, thread A will release the lock on the object and enter the wait pool for the object.

2) The difference between notify() and notifyAll()

① If a thread calls wait() on an object, the thread will be in the object’s wait pool. The threads in the wait pool will not compete for the lock.

② When a thread invokes notifyAll() or notify(), the awakened thread enters the lock pool of the object, and the threads in the lock pool compete for the lock. NotifyAll () moves all the threads in the wait pool to the lock pool, and notifyAll() moves all the threads in the wait pool to the lock pool.

NotifyAll () is used to wake up all wait threads; Only one wait thread can be awakened randomly by notify().

③ Threads with higher priorities have a higher probability of competing for object locks. If a thread does not compete for the object lock, it remains in the lock pool. The wait() method will only be called again by the thread before it returns to the wait pool. The thread competing for the object lock continues to execute until the synchronized method or block is finished, which releases the object lock, at which point the thread in the lock pool continues to compete for the object lock.

NotifyAll () moves all threads from the wait pool to the lock pool to compete for a lock. NotifyAll () notifyAll() moves all threads from the wait pool to the lock pool to compete for a lock. NotifyAll () notifyAll() moves all threads from the wait pool to the lock pool to compete for a lock.

3) Why may notify() cause a deadlock, but notifyAll() does not

Notify () wakes up only one thread that is waiting and releases the lock on the object when the thread completes execution. If notify() is not executed again, other threads that are waiting will remain in the waiting state and will not be awakened to enter the lock pool of the object, resulting in a deadlock. NotifyAll (), however, does not cause a deadlock problem.

What are the ways to create a thread pool?

Java provides four methods for creating a thread pool through Executors:

(1) newCachedThreadPool: Create a cacheable thread pool. If the length of the thread pool exceeds the processing requirements, you can recycle idle threads flexibly. If there are no threads to recycle, create a new thread.

(2) newFixedThreadPool: create a thread pool of fixed length, control the maximum number of concurrent threads, exceeding the threads will wait in the queue;

③newScheduledThreadPool: Creates a thread pool of fixed length to support scheduled and periodic task execution.

④newSingleThreadExecutor: create a single thread pool, it will only use a single worker thread to execute tasks, ensure that all tasks are executed in the specified order (FIFO, LIFO, priority).

What are the core parameters of the thread pool method? What does each parameter mean?

See this article for details on thread pools

10,volatileThe role of keywords

Function:

Ensure that variables that are volatile are visible to other threads immediately if their values change.

② Instruction reordering is prohibited. Instruction reordering occurs in multithreading, which means that the order of code execution may change after compiler optimization. Instance objects in lazy singletons, for example, need to be volatile, otherwise two objects may be instantiated.

Note: The volatile keyword does not guarantee atomicity.

  • Why do dirty reads occur?

① The Java memory model specifies that variables are stored in main memory, and that each thread has its own working memory. All operations by threads on variables must be done in working memory, not directly in main memory, and each thread cannot access the working memory of other threads. It is possible that thread A has changed the value of A variable, but thread B doesn’t know about it, so thread B is still using thread A to change the value of A variable, and thread B is using dirty data.

② To solve this problem, declare the variable volatile. This tells the JVM that the variable is volatile and must be read from main memory each time it is used. Main memory is a shared area, operating on main memory, visible to other threads.

11,synchronizedThe role of keywords

Synchronized means synchronized. A synchronized modified method or block of code can only be executed by one thread at a time.

Usage:

Lock this; lock this;

public synchronized void method(a) {}
Copy the code

(2) Modify code block: use the lock by yourself, as long as the object can be;

Object obj = new Object()
    
synchronized (obj){
/ /...
}
Copy the code

(3) Modify static methods: the lock used is the bytecode file of the method class.

public static synchronized void sale(a) {}
Copy the code
12. Upgrade the synchronized lock

First, let’s understand the concept of related locks:

Spin locking (CAS) : Allows threads that do not meet a condition to wait to see if they can acquire the lock to avoid the overhead of thread switching by consuming CPU time. There is a limit to how long or how many spins can wait, and if the spin exceeds the defined time and the lock is not acquired, the thread should be suspended. After JDK1.6, adaptive spin locking was introduced, which means that the number of spins is not fixed, but determined by the time of the previous spin on the same lock and the state of the lock owner. If the spin wait has just successfully acquired a lock on the same lock object, and the thread holding the lock is running, the JVM will assume that the spin is likely to succeed again, allowing the spin wait to last a relatively long time. If spin is rarely successfully acquired for a lock, it is possible to omit the spin process and block the thread directly in future attempts to acquire the lock, avoiding wasting processor resources.

Biased locks: In most cases, locks are always acquired multiple times by the same thread. When a thread accesses a block and acquires a lock, the thread ID of the lock bias is stored in the lock record in the object header and stack frame. The bias lock is a reentrant lock. If the Mark Word of the lock object header stores biased locks to the current thread, there is no need to re-perform the CAS operation to lock and unlock. The thread holding the biased lock (which is not active) releases the lock when another thread attempts to contest the biased lock. Biased locks cannot be optimized with spin locks because the assumption of biased locks is broken and upgraded to lightweight locks as soon as another thread applies for the lock.

Heavyweight lock: Implemented through the internal monitor of an object. Monitor is essentially implemented by MutexLock of the underlying operating system. Switching between threads implemented by the operating system requires switching from user state to kernel state, and the switching cost is very high. Thread contention does not use spin and does not consume CPU. However, a thread enters a block and waits until it is woken up by another thread, with slow response times.

Lightweight locks: Reduce the performance cost of using heavyweight locks when there is no real competition. The JVM creates a spatial LockRecord in the stack frame of the current thread to store the LockRecord, copies the Mark Word from the object header into the LockRecord, and points the Owner pointer in the LockRecord to the lock object. The thread then attempts to use CAS to replace the Mark Word in the object header with a pointer to the lock record. If successful, the current thread acquires the lock; if failed, other threads compete for the lock, and the current thread attempts to acquire the lock by spinning. Failure to acquire the spin lock increases the lock expansion to a heavyweight lock.

What is a deadlock?

Thread deadlock refers to the situation that two or more threads hold resources needed by each other, so that these threads are in the waiting state and cannot execute normally.

As shown in the figure below, thread A holds resource 2 and thread B holds resource 1. They both want to claim resources from each other at the same time, so the two threads wait for each other and enter A deadlock state.