Analysis of 22 high-frequency interview questions in Java multithreading

Analysis of 22 high-frequency interview questions in Java multithreading

As a Java developer, multithreading is an inescapable topic, whether it is work or interview, but it is more vague and difficult to understand, because multithreaded programs are more difficult to observe and track when they are running. Understand the knowledge of multithreading and concurrency, you can open up the gap with the people around you during the interview, and you can know it well when you code.

In addition, I have collected more than 20 years of company interview knowledge points, and various Java core knowledge points are free to share with you. I think it is very useful for interviews. If you want information, please click 795983544 secret code nuggets.

1. There are several ways to implement multithreading in Java

(1) Inherit the Thread class;

(2) Implement the Runnable interface;

(3) Implement Callable interface to create Thread thread through FutureTask wrapper;

(4) Use ExecutorService, Callable, and Future to implement multithreading with returned results (that is, use ExecutorService to manage the previous three methods).

2. How to stop a running thread

(1) Use the exit flag to make the thread exit normally, that is, the thread terminates when the run method is completed.

(2) Use the stop method to forcibly terminate, but this method is not recommended, because stop and suspend and resume are both expired and invalidated methods.

(3) Use the interrupt method to interrupt the thread.

class MyThread extends Thread {
    volatile Boolean stop = false;
    public void run() {
        while (!stop) {
            System.out.println(getName() + " is running");
            try {
                sleep(1000);
            }
            catch (InterruptedException e) {
                System.out.println("week up from blcok...");
                stop = true;
                // 
            }
        }
        System.out.println(getName() + " is exiting...");
    }
}
class InterruptThreadDemo3 {
    public static void main(String[] args) throws InterruptedException {
        MyThread m1 = new MyThread();
        System.out.println("Starting thread...");
        m1.start();
        Thread.sleep(3000);
        m1.interrupt();
        // 
        Thread.sleep(3000);
        //  3   m1  
        System.out.println("Stopping application...");
    }
}
 

3. What is the difference between notify() and notifyAll()?

Notify may cause deadlock, while notifyAll will not

Only one thread can acquire the lock at any time, that is to say, only one thread can run the code in synchronized. Use notifyall to wake up all threads in the wait state and reenter the lock contention queue, while notify can only wake up one.

wait() should be used in conjunction with the while loop. If should not be used, the condition must be checked before and after the wait() call. If it is not met, you must call notify() to wake up another thread for processing, and continue to wait() until the condition is met. Execute down.

Notify() is an optimization of notifyAll(), but it has very precise application scenarios and requires correct use. Otherwise it may lead to deadlock. The correct scenario should be that the waiting in WaitSet is the same condition, any one of them can be awakened to correctly handle the following matters, if the awakened thread cannot be processed correctly, be sure to continue to notify() the next thread, and you need to return to the next thread. WaitSet.

4. What is the difference between sleep() and wait()?

For the sleep() method, we must first know that the method belongs to the Thread class. The wait() method belongs to the Object class.

The sleep() method causes the program to suspend execution for the specified time, giving up the other thread of the cpu, but its monitoring state is still maintained, and it will automatically resume the running state when the specified time is up. In the process of calling the sleep() method, the thread will not release the object lock.

When the wait() method is called, the thread will give up the object lock and enter the waiting lock pool waiting for this object. Only after the notify() method is called for this object, the thread enters the object lock pool preparation and acquires the object lock and enters the running state.

5. What is volatile? Can order be guaranteed?

Once a shared variable (class member variable, class static member variable) is modified by volatile, then it has two layers of semantics:

(1) To ensure the visibility of different threads operating on this variable, that is, a thread modifies the value of a variable, the new value is immediately visible to other threads, the volatile keyword will force the modified value Write to main memory immediately.

(2) Reordering of instructions is prohibited.

Volatile is not an atomic operation

What is guaranteed partial order?

When the program executes the read operation or write operation of the volatile variable, all the changes in the previous operation must have been carried out, and the result has been visible to the following operation; the operation behind it must have not been carried out;

x = 2;// 1
y = 0;// 2
flag = true;// 3
x = 4;// 4
y = -1;// 5
 

Since the ag variable is a volatile variable, in the process of instruction reordering, statement 3 will not be placed before statement 1 and statement 2, and statement 3 will not be placed after statement 4 and statement 5. But note that the order of statement 1 and statement 2, and the order of statement 4 and statement 5 are not guaranteed.

Using Volatile is generally used for the double check lock of the state tag amount and singleton mode

6. What is the difference between the start() and run() methods in the Thread class?

The start() method is used to start the newly created thread, and the run() method is called internally in start(), which is not the same as calling the run() method directly. When you call the run() method, it will only be called in the original thread. If no new thread is started, the start() method will start the new thread.

7. Why wait, notify and notifyAll are not in the thread class?

The obvious reason is that the locks provided by JAVA are object-level rather than thread-level, and each object has a lock, which is obtained through a thread. If the thread needs to wait for some locks, it makes sense to call the wait() method in the object. If the wait() method is defined in the Thread class, it is not obvious which lock the thread is waiting for. Simply put, because wait, notify, and notifyAll are lock-level operations, they are defined in the Object class because the lock belongs to the object.

8. Why should the wait and notify methods be called in a synchronized block?

(1) Only when the calling thread owns the exclusive lock of an object, can the wait(), notify() and notifyAll() methods of the object be called.

(2) If you don't do this, your code will throw an IllegalMonitorStateException.

(3) Another reason is to avoid race conditions between wait and notify.

The wait() method forces the current thread to release the object lock. This means that before calling the wait() method of an object, the current thread must have acquired the object's lock. Therefore, the thread must be in the synchronization method or synchronization code block of an object to call the wait() method of the object.

Before calling the notify() and notifyAll() methods of the object, the calling thread must have obtained the lock of the object. Therefore, the notify() or notifyAll() method of the object must be called in the synchronization method or synchronization code block of an object.

The reason for calling the wait() method is usually that the calling thread wants to continue execution after a special state (or variable) is set. The reason for calling the notify() or notifyAll() method is usually that the calling thread wants to tell other waiting threads: "The special state has been set." This state is used as a communication channel between threads, and it must be a mutable shared state (or variable).

9. What is the difference between interrupted and isInterruptedd methods in Java?

The main difference between interrupted() and isInterrupted() is that the former will clear the interrupted state while the latter will not. The interrupt mechanism of Java multi-threading is implemented with internal flags. Calling Thread.interrupt() to interrupt a thread will set the interrupt flag to true. When the interrupted thread calls the static method Thread.interrupted() to check the interrupt status, the interrupt status will be cleared. The non-static method isInterrupted() is used to query the interrupt status of other threads without changing the interrupt status flag. Simply put, any method that throws InterruptedException will clear the interrupt status. In any case, the interrupt status of a thread may be changed by other threads calling interrupts.

10. What is the difference between synchronized and ReentrantLock in Java?

Similarity:

These two synchronization methods have many similarities. They are both locked synchronization and blocking synchronization. That is to say, when a thread obtains an object lock and enters the synchronization block, the other access to the synchronization block Threads must be blocked waiting outside the synchronized block, and the cost of thread blocking and wake-up is relatively high.

the difference:

The biggest difference between these two methods is that for Synchronized, it is a keyword of the java language and a mutual exclusion at the native syntax level, which requires jvm implementation. ReentrantLock is an API-level mutex lock provided after JDK 1.5, which requires lock() and unlock() methods to cooperate with try/finally statement blocks.

After Synchronized has been compiled, two bytecode instructions, monitorrenter and monitorexit, will be formed before and after the synchronization block. When executing the monitorenter instruction, first try to acquire the object lock. If the object is not locked, or the current thread already owns the object lock, increase the lock counter by 1. Correspondingly, when the monitorexit instruction is executed, the lock counter is decreased by 1. When the counter is 0, the lock Was released. If acquiring the object lock fails, the current thread will block until the object lock is released by another thread.

Since ReentrantLock is a set of mutual exclusion locks provided under the java.util.concurrent package, compared to Synchronized, the ReentrantLock class provides some advanced functions, mainly the following 3 items:

(1) Waiting can be interrupted. When the thread holding the lock is not released for a long time, the waiting thread can choose to give up waiting, which is equivalent to Synchronized and can avoid deadlock.

(2) Fair locks. When multiple threads are waiting for the same lock, they must acquire the locks in the order of the time they apply for the lock. Synchronized locks are not fair locks. The default constructor of ReentrantLock is an unfair lock created, which can be set to fair through the parameter true. Lock, but the performance of fair lock is not very good.

(3) The lock binds multiple conditions, and a ReentrantLock object can bind to two objects at the same time.

11. There are three threads T1, T2, T3, how to ensure sequential execution?

In multithreading, there are many ways to make threads execute in a specific order. You can use the join() method of the thread class to start another thread in one thread, and the other thread finishes the thread and continues execution. To ensure the order of the three threads, you should start the last one first (T3 calls T2, T2 calls T1), so that T1 will complete first and T3 will complete last.

In fact, it does not matter which of the three threads is started first, because the join method is used in the run method of each thread to limit the execution order of the three threads.

public class JoinTest2 {
    //1.  T1 T2 T3   T2   T1  T3   T2  
    public static void main(String[] args) {
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1");
            }
        }
        );
        @Override
        public void run() {
            try {
                //  t1   t1  
                t1.join();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2");
        }
    }
    );
    Thread t3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //  t2   t2  
                t2.join();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t3");
        }
    }
    );
    t3.start();
    //
    t2.start();
    t1.start();
}
}
 

12. What is the difference between SynchronizedMap and ConcurrentHashMap?

SynchronizedMap(), like Hashtable, synchronizes the entire map when calling all map methods. The implementation of ConcurrentHashMap is more refined, it locks all buckets in the map. Therefore, as long as one thread accesses the map, other threads cannot enter the map, and if a thread accesses a bucket of ConcurrentHashMap, other threads can still perform certain operations on the map.

Therefore, ConcurrentHashMap is obviously more advantageous than Collections.synchronizedMap() in terms of performance and security. At the same time, the synchronization operation is accurately controlled to the bucket, so that even when traversing the map, if other threads try to modify the map, it will not throw a ConcurrentModi cationException.

13. What is thread safety

Thread safety means that multiple threads access the same code without generating uncertain results.

In a multi-threaded environment, when the threads do not share data, that is, they are all private members, then they must be thread-safe. But this kind of situation is rare. In most cases, data needs to be shared. At this time, proper synchronization control is required.

Thread safety generally involves synchronized, that is, a piece of code can only have one thread to operate at the same time, otherwise the intermediate process may produce unpredictable results.

If your code is in a process where multiple threads are running at the same time, these threads may run this code at the same time. If the ArrayList is not thread-safe every time it runs.

14. What is the function of the yield method in the Thread class?

The Yield method can suspend the currently executing thread object, allowing other threads of the same priority to execute. It is a static method and only guarantees that the current thread will give up the CPU occupation but cannot guarantee that other threads will be able to occupy the CPU. The thread that executes yield() may be executed immediately after it enters the suspended state.

15. What is the difference between the submit() and execute() methods in the Java thread pool?

Both methods can submit tasks to the thread pool. The return type of the execute() method is void, which is defined in the Executor interface, while the submit() method can return a Future object holding the calculation result, which is defined in the ExecutorService interface. It extends the Executor interface, and other thread pool classes like ThreadPoolExecutor and ScheduledThreadPoolExecutor have these methods.

16. Talk about your understanding of the synchronized keyword

The synchronized keyword solves the synchronization of accessing resources between multiple threads. The synchronized keyword can ensure that the method or code block modified by it can only be executed by one thread at any time.

In addition, in the early version of Java, synchronized is a heavyweight lock, which is inefficient, because the monitor lock (monitor) is implemented by relying on the Mutex Lock of the underlying operating system, and the thread of Java is one of the native threads mapped to the operating system. Up. If you want to suspend or wake up a thread, you need the help of the operating system, and the operating system needs to switch from the user mode to the kernel mode when switching between threads. The transition between this state requires a relatively long time and time cost. Relatively high, which is why the early synchronized is inefficient. Fortunately, after Java 6 the Java official has greatly optimized synchronized from the JVM level, so the current synchronized lock efficiency is also optimized very well. JDK1.6 introduces a large number of optimizations to the implementation of locks, such as spin locks, adaptive spin locks, lock elimination, lock coarsening, biased locks, lightweight locks and other technologies to reduce the overhead of lock operations.

17. Tell me how I use the synchronized keyword, have you used it in the project?

The three main ways to use the synchronized keyword:

(1) Modification instance method: Act on the current object instance to lock, obtain the current object instance lock before entering the synchronization code

(2) Modification of static methods: that is, to lock the current class, which will act on all object instances of the class, because static members do not belong to any instance object, and are class members (static indicates that this is a static resource of the class, regardless of How many objects are new, only one copy). So if a thread A calls a non-static synchronized method of an instance object, and thread B needs to call the static synchronized method of the class to which the instance object belongs, it is allowed and mutual exclusion will not occur, because the lock occupied by the static synchronized method is The lock of the current class, and the lock occupied by accessing the non-static synchronized method is the lock of the current instance object.

(3) Modified code block: Specify the lock object, lock the given object, and obtain the lock of the given object before entering the synchronization code base.

Summary: The synchronized keyword added to the static static method and synchronized (class) code block is to lock the Class class. The synchronized keyword is added to the instance method to lock the object instance. Try not to use synchronized(String a) because in the JVM, the string constant pool has a caching function!

18. What is thread safety? Is Vector a thread safe class?

If your code is in a process where multiple threads are running at the same time, these threads may run this code at the same time. If every time

The row result is the same as the result of a single-threaded operation, and the values of other variables are also the same as expected, which is thread-safe.

19. What is the role of the volatile keyword?

Once a shared variable (class member variable, class static member variable) is modified by volatile, then it has two layers of semantics:

(1) The visibility of this variable is guaranteed when different threads operate, that is, if a thread modifies the value of a variable, the new value is immediately visible to other threads.

(2) Reordering of instructions is prohibited.

(3) Volatile essentially tells the jvm that the value of the current variable in the register (working memory) is uncertain and needs to be read from the main memory; synchronized is to lock the current variable, only the current thread can access the variable, other threads Was blocked.

(4) Volatile can only be used at the variable level; synchronized can be used at the variable, method, and class level.

(5) Volatile can only realize the visibility of the modification of variables, but cannot guarantee the atomicity; synchronized can guarantee the visibility and atomicity of the modification of variables.

(6) Volatile will not cause thread blocking; synchronized may cause thread blocking.

(7) Variables marked with volatile will not be optimized by the compiler; variables marked with synchronized can be optimized by the compiler.

20. What are the commonly used thread pools?

(1) newSingleThreadExecutor: Create a single-threaded thread pool, this thread pool ensures that the execution order of all tasks is executed in the order of submission of tasks.

(2) newFixedThreadPool: Create a fixed-size thread pool, and create a thread every time a task is submitted until the thread reaches the maximum size of the thread pool.

(3) newCachedThreadPool: Create a cacheable thread pool. This thread pool does not limit the size of the thread pool. The size of the thread pool depends entirely on the maximum thread size that the operating system (or JVM) can create.

(4) newScheduledThreadPool: Create a thread pool of unlimited size, this thread pool supports the needs of timing and periodic execution of tasks.

(5) newSingleThreadExecutor: Create a single-threaded thread pool. This thread pool supports the needs of timed and periodic execution of tasks.

21. Briefly describe your understanding of thread pools

(If you ask such a question, you can expand on how to use the thread pool, the benefits of the thread pool, and the startup strategy of the thread pool) Reasonable use of the thread pool can bring three benefits.

(1) Reduce resource consumption. Reduce the consumption caused by thread creation and destruction by reusing the created threads.

(2) Improve response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.

(3) Improve the manageability of threads. Threads are scarce resources. If they are created unlimitedly, they will not only consume system resources, but also reduce the stability of the system. The thread pool can be used for unified allocation, tuning and monitoring.

22. How is the Java program executed?

In our daily work, we use development tools (IntelliJ IDEA or Eclipse, etc.) to debug the program easily, or package the project into a jar package or war package through a packaging tool, and put it into a Web container such as Tomcat to run normally.

(1) First compile Java code into bytecode, that is, compile .java type files into .class type files. The general execution flow of this process: Java source code -> lexical analyzer -> syntax analyzer -> semantic analyzer -> character code generator -> finally generate bytecode, any node execution failure will cause compilation failure

(2) Place the class file on the Java virtual machine, this virtual machine usually refers to the official Hotspot JVM that comes with Oracle;

(3) Java virtual machine uses Class Loader to load class files;

(4) After the class is loaded, the bytecode verification will be performed. After the bytecode verification is passed, the JVM interpreter will translate the bytecode into machine code and send it to the operating system for execution. But not all code is interpreted and executed. JVM optimizes this. For example, in the case of Hotspot virtual machine, it itself provides JIT (Just In Time), which is what we usually call a dynamic compiler, which can be used in The hot code is compiled into machine code at runtime, and the bytecode becomes compiled and executed at this time. The Java program execution flow chart is as follows:

At last

Multithreading and high concurrency is a technical point that must be asked in interviews in some Internet companies, so you must pay attention to the key points during the interview and think about some high-concurrency and high-availability technologies. During the interview, you must master the rhythm, talk about some techniques that make the interviewer's eyes bright, and talk less about some basic things. After all, the interviewer has heard so many faces, and the more rare, the more exciting the interview. The interest of the officials is then mastered in their own rhythm.

In addition, I have collected more than 20 years of company interview knowledge points, and various Java core knowledge points are free to share with you. I think it is very useful for interviews. If you want information, please click 795983544 secret code nuggets.