java basics

1. Could you please talk about your understanding of volatile?

Volatile is a lightweight synchronization mechanism provided by JVM

1. Ensure visibility

2. No guarantee of atomicity

3. Prohibition of order rearrangement

<1> volatile guarantees visibility: (JMM memory model)

JMM (Java memory model) itself is an abstract concept and does not really exist, it describes a set of rules or specifications.

JMM regulations on synchronization:

  1. Before the thread is unlocked, the value of the shared variable must be flushed back to the main memory
  2. Before the thread locks, it must read the latest value of the main memory to its own working memory
  3. Locking and unlocking are the same lock

Since the entity of the JVM running program is the thread, and each thread is created, the JVM will create a working memory (stack space) for it. The working memory is the private data area of ​​each thread, and the Java memory model stipulates that all variables are stored in Main memory, main memory is a shared memory area, all threads can access. Thread operations on variables (read assignment, etc.) must be performed in the working memory, first copy the variable from the main memory to its own working memory space, then operate the variable, and then write the variable back to the main memory after the operation is completed , Cannot directly manipulate variables in the main memory, and different threads cannot access the working memory sent by each other.

And this may exist when a thread AAA modifies the value of the shared variable X but has not written back to the main memory, another thread BBB operates on the same variable X in the main memory, but at this time the value of the AAA thread’s working memory The variable X is not a courseware for the thread BBB. This delay in synchronization between the working memory and the main memory causes visibility problems.

<2> Volatile does not guarantee atomicity:

Atomicity: indivisibility and integrity, that is, when a thread is doing a specific business, it cannot be blocked or divided in the middle. It needs to be completed as a whole, and either succeeds or fails at the same time.

class MyData2 {    /**     * volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知     */    volatile int number = 0;      public void addPlusPlus() {        number ++;    }} public class VolatileAtomicityDemo { 	public static void main(String[] args) {        MyData2 myData = new MyData2();         // 创建10个线程,线程里面进行1000次循环        for (int i = 0; i < 20; i++) {            new Thread(() -> {                // 里面                for (int j = 0; j < 1000; j++) {                    myData.addPlusPlus();                }            }, String.valueOf(i)).start();        }         // 需要等待上面20个线程都计算完成后,在用main线程取得最终的结果值        // 这里判断线程数是否大于2,为什么是2?因为默认是有两个线程的,一个main线程,一个gc线程        while(Thread.activeCount() > 2) {            // yield表示不执行            Thread.yield();        }         // 查看最终的值        // 假设volatile保证原子性,那么输出的值应该为:  20 * 1000 = 20000        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number); 	} } 

The final result is always less than 20000.

number++It is not thread-safe under multithreading.

We can compile the code into bytecode, it can be seen that it number++is compiled into 3 instructions.

Assuming that we did not add synchronized, then the first step may exist. Three threads use the getfield command at the same time to get the value of n in the main memory, and then the three threads each perform an increase by 1 operation in their own working memory, but they When the iadd command is executed concurrently, because only one can be written, other operations will be suspended. Assume that one thread performs the write operation first. After writing, the visibility of volatile should be told to the other two threads. The value of the main memory has been modified, but because it was too fast, the other two threads successively executed the iadd command to perform the write operation, which caused the other threads to not accept the change of the main memory n, thus overwriting the original Value, write loss occurs, so that the final result is less than 20000.
problem solved:

  • You can add synchronized to solve it, but it is a heavyweight synchronization mechanism, and there are concerns about performance.
  • How to solve the problem that number++ is not thread-safe under multithreading without synchronized? Use AtomicInteger
import java.util.concurrent.atomic.AtomicInteger; class MyData2 {    /**     * volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知     */	volatile int number = 0;	AtomicInteger number2 = new AtomicInteger();     public void addPlusPlus() {        number ++;    }        public void addPlusPlus2() {    	number2.getAndIncrement();    }} public class VolatileAtomicityDemo { 	public static void main(String[] args) {        MyData2 myData = new MyData2();         // 创建10个线程,线程里面进行1000次循环        for (int i = 0; i < 20; i++) {            new Thread(() -> {                // 里面                for (int j = 0; j < 1000; j++) {                    myData.addPlusPlus();                    myData.addPlusPlus2();                }            }, String.valueOf(i)).start();        }         // 需要等待上面20个线程都计算完成后,在用main线程取得最终的结果值        // 这里判断线程数是否大于2,为什么是2?因为默认是有两个线程的,一个main线程,一个gc线程        while(Thread.activeCount() > 2) {            // yield表示不执行            Thread.yield();        }         // 查看最终的值        // 假设volatile保证原子性,那么输出的值应该为:  20 * 1000 = 20000        System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);        System.out.println(Thread.currentThread().getName() + "\t finally number2 value: " + myData.number2);	}}

The output is:

main	 finally number value: 18766main	 finally number2 value: 20000

<3> Volatile prohibits instruction rearrangement

When a computer is executing a program, in order to improve performance, the compiler and processor often rearrange instructions, generally divided into the following three types:

In a single-threaded environment, ensure that the final execution result of the program is consistent with the result of the sequential execution of the code.

In a multi-threaded environment, threads are alternately executed. Due to the existence of compiler optimization rearrangement, it is impossible to determine whether the variables used in the two linearizations can guarantee consistency, and the result is unpredictable.

public class ReSortSeqDemo{	int a = 0;	boolean flag = false;    	public void method01(){		a = 1;//语句1		flag = true;//语句2	}        public void method02(){        if(flag){            a = a + 5; //语句3        }        System.out.println("retValue: " + a);//可能是6或1或5或0    }    }

In a multi-threaded environment, threads alternately execute method01()and method02(), due to the existence of compiler optimization rearrangement, it is impossible to determine whether the variables used in the two threads can guarantee consistency, and the result is unpredictable.

Volatile implementation prohibits instruction retake optimization, thereby avoiding out-of-order execution of programs in a multi-threaded environment.

First understand a concept, the memory barrier (Memory Barrier), also known as the memory barrier, is a CPU instruction, it has two functions:

  1. Ensure the order of execution of specific operations,
  2. Ensure memory visibility of certain variables (use this feature to achieve volatile memory visibility).

Because both the compiler and the processor can perform instruction rearrangement optimization. If a Memory Barrier is inserted between instructions, it will tell the compiler and CPU that no instructions can be reordered with this Memory Barrier instruction, that is to say, by inserting a memory barrier, the instructions before and after the memory barrier are prohibited from performing reordering optimization. Another function of the memory barrier is to force the cache data of various CPUs to be flushed out, so any thread on the CPU can read the latest version of these data.

When writing a volatile variable, a store barrier instruction will be added after the write operation to flush the shared variable value in the working memory back to the main memory.

When the Volatile variable is read, a load barrier instruction is added before the read operation to read the shared variable from the main memory.

2. CAS you know?

The full name of CAS is Compare-And-Swap, which is a CPU concurrency primitive.

CAS has 3 operands, the memory value V, the old expected value A, and the updated value B to be modified.
If and only if the expected value A and the memory value V are the same, modify the memory value V to B, otherwise do nothing.

public class CASDemo{    public static void main(string[] args){        AtomicInteger atomicInteger = new AtomicInteger(5);// mian do thing. . . . ..        System.out.println(atomicInteger.compareAndSet(5, 2019)+"\t current data: "+atomicInteger.get());        System.out.println(atomicInteger.compareAndset(5, 1024)+"\t current data: "+atomicInteger.get());    }}

The output result is

true    2019false   2019

The underlying principle of CAS? Talk about your understanding of UnSafe?

atomiclnteger.getAndIncrement();Source code

public class AtomicInteger extends Number implements {    private static final long serialVersionUID = 6214790243416807050L;     // setup to use Unsafe.compareAndSwapInt for updates    private static final Unsafe unsafe = Unsafe.getUnsafe();    private static final long valueOffset;     static {        try {            valueOffset = unsafe.objectFieldOffset                (AtomicInteger.class.getDeclaredField("value"));        } catch (Exception ex) { throw new Error(ex); }    }     private volatile int value;        /**     * Creates a new AtomicInteger with the given initial value.     *     * @param initialValue the initial value     */    public AtomicInteger(int initialValue) {        value = initialValue;    }     /**     * Creates a new AtomicInteger with initial value {@code 0}.     */    public AtomicInteger() {    }        ...                /**     * Atomically increments by one the current value.     *     * @return the previous value     */    public final int getAndIncrement() {        return unsafe.getAndAddInt(this, valueOffset, 1);    }        ...}    

1. UnSafe is the core class of CAS. Since the java method cannot directly access the underlying system, it needs to be accessed through the native method. Based on the UnSafe class, the data in the specific memory can be directly manipulated. The UnSafe class exists in the sun.misc package. The internal method operation can directly manipulate the memory like a pointer of c, because the execution of the CAS operation in java depends on the method of the UnSafe class.

2. The variable valueOffset represents the offset address of the variable value in the memory, because UnSafe obtains data based on the memory offset address.

3. The variable value is modified with volatile to ensure memory visibility between multiple threads.

What is CAS?

The full name of CAS is Compare-And-Swap, which is a CPU concurrency primitive.
His function is to judge whether the value of a certain location in the memory is the expected value, and if it is, update it to a new value. This process is atomic.

The CAS concurrency primitive embodied in the JAVA language is the various methods in the sun.misc.Unsafe class. Call the CAS method in the UnSafe class, and the JVM will help us implement the CAS assembly instruction. This is a function that is completely dependent on hardware, through which atomic operations are realized. Again, since CAS is a system primitive, the primitive belongs to the category of operating system terms. It is composed of several instructions to complete a process of a certain function, and the execution of the primitive must be continuous. The process is not allowed to be interrupted, which means that CAS is an atomic instruction of the CPU and will not cause the so-called data inconsistency problem. (Atomic)

The above is similar to a spin lock

UnSafe.getAndAddInt() source code explanation:

  • The var1 AtomicInteger object itself.
  • var2 The object is worth a reference address.
  • var4 The amount to be changed. (Step size)
  • var5 is the real value found in main memory after using var1 and var2.
  • Use the current value of the object to compare with var5: If they are the same, update var5+var4 and return true, if they are different, continue to get the value and compare again until the update is complete.

Assume that two threads, thread A and thread B, execute the getAndAddInt operation at the same time (running on different CPUs respectively):

  1. The original value of value in Atomiclnteger is 3, that is, the value of Atomiclnteger in main memory is 3. According to the JMM model, thread A and thread B each hold a copy of the value of 3 to their respective working memory.
  2. Thread A gets the value 3 through getIntVolatile(var1, var2), and then thread A is suspended.
  3. Thread B also obtains the value of 3 through the getintVolatile(var1, var2) method. At this time, thread B is not suspended and executes the compareAndSwapInt method to compare the memory value to 3. It successfully modifies the memory value to 4, and thread B is finished. Everything is OK.
  4. At this time, thread A resumes, performs the compareAndSwapInt method comparison, and finds that the value number 3 in its hand is inconsistent with the value number 4 in the main memory, indicating that the value has been modified by other threads first, and the modification of thread A failed this time. Can only re-read it again.
  5. Thread A re-acquires the value value, because the variable value is modified by volatile, so other threads modify it, thread A can always see, thread A continues to execute compareAndSwaplnt to compare and replace until it succeeds

Disadvantages of CAS:

1. Long cycle time is expensive

// ursafe.getAndAddIntpublic final int getAndAddInt(Object var1, long var2, int var4){	int var5;	do {		var5 = this.getIntVolatile(var1, var2);	}while(!this.compareAndSwapInt(varl, var2, var5,var5 + var4));    return var5;}

It can be seen that when the getAndAddInt method is executed, there is a do while. If the CAS fails, it will keep trying. If the CAS is unsuccessful for a long time, it may bring a lot of overhead to the CPU.

2. Only the atomic operation of a shared variable can be guaranteed

When performing operations on a shared variable, we can use cyclic CAS to ensure atomic operations, but when operating on multiple shared variables, cyclic CAS cannot guarantee the atomicity of the operation. At this time, locks can be used to ensure atomicity. .

3.CAS causes ABA problems

3. Talk about the ABA problem of the atomic class AtomicInteger? Do you know the atomic class update reference?

CAS can cause "ABA problems" (civet cats for princes).

For example, a thread one fetches A from memory location V, at this time another thread two also fetches A from memory, and thread two performs some operations to become B, and then thread two changes the data at location V to A. At this time, when thread one performs CAS operation, it is found that there is still A in the memory, and then thread one is successful.

Although the CAS operation of thread one is successful, it does not mean that the process is without problems.

AtomicReference atomic reference (custom class, the principle is similar to AtomicInteger)

import java.util.concurrent.atomic.AtomicReference; class User{		String userName;		int age;	    public User(String userName, int age) {		this.userName = userName;		this.age = age;	} 	@Override	public String toString() {		return String.format("User [userName=%s, age=%s]", userName, age);	}    } public class AtomicReferenceDemo {    public static void main(String[] args){        User z3 = new User( "z3",22);        User li4 = new User("li4" ,25);		AtomicReference<User> atomicReference = new AtomicReference<>();        atomicReference.set(z3);		System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());    }} 

Output result

true	User [userName=li4, age=25]false	User [userName=li4, age=25]

AtomicStampedReference version number atomic reference:

Atomic reference + a new mechanism is to modify the version number (similar to a timestamp), which is used to solve the ABA problem.

import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;import java.util.concurrent.atomic.AtomicStampedReference; public class ABADemo {	/**	 * 普通的原子引用包装类	 */	static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); 	// 传递两个值,一个是初始值,一个是初始版本号	static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1); 	public static void main(String[] args) { 		System.out.println("============以下是ABA问题的产生=========="); 		new Thread(() -> {			// 把100 改成 101 然后在改成100,也就是ABA			atomicReference.compareAndSet(100, 101);			atomicReference.compareAndSet(101, 100);		}, "t1").start(); 		new Thread(() -> {			try {				// 睡眠一秒,保证t1线程,完成了ABA操作				TimeUnit.SECONDS.sleep(1);			} catch (InterruptedException e) {				e.printStackTrace();			}			// 把100 改成 101 然后在改成100,也就是ABA			System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get()); 		}, "t2").start(); 		/		try {			TimeUnit.SECONDS.sleep(2);		} catch (Exception e) {			e.printStackTrace();		}		/ 				System.out.println("============以下是ABA问题的解决=========="); 		new Thread(() -> { 			// 获取版本号			int stamp = atomicStampedReference.getStamp();			System.out.println(Thread.currentThread().getName() + "\t 第一次版本号" + stamp); 			// 暂停t3一秒钟			try {				TimeUnit.SECONDS.sleep(1);			} catch (InterruptedException e) {				e.printStackTrace();			} 			// 传入4个值,期望值,更新值,期望版本号,更新版本号			atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(),					atomicStampedReference.getStamp() + 1); 			System.out.println(Thread.currentThread().getName() + "\t 第二次版本号" + atomicStampedReference.getStamp()); 			atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(),					atomicStampedReference.getStamp() + 1); 			System.out.println(Thread.currentThread().getName() + "\t 第三次版本号" + atomicStampedReference.getStamp()); 		}, "t3").start(); 		new Thread(() -> { 			// 获取版本号			int stamp = atomicStampedReference.getStamp();			System.out.println(Thread.currentThread().getName() + "\t 第一次版本号" + stamp); 			// 暂停t4 3秒钟,保证t3线程也进行一次ABA问题			try {				TimeUnit.SECONDS.sleep(3);			} catch (InterruptedException e) {				e.printStackTrace();			} 			boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1); 			System.out.println(Thread.currentThread().getName() + "\t 修改成功否:" + result + "\t 当前最新实际版本号:"					+ atomicStampedReference.getStamp()); 			System.out.println(Thread.currentThread().getName() + "\t 当前实际最新值" + atomicStampedReference.getReference()); 		}, "t4").start(); 	}}

Output result

============以下是ABA问题的产生==========true    2019============以下是ABA问题的解决==========t3     第一次版本号1t4     第一次版本号1t3     第二次版本号2t3     第三次版本号3t4     修改成功否:false     当前最新实际版本号:3t4     当前实际最新值100
The realization and function of reflection

After the Java language is compiled, a .class file is generated, and reflection is to find a certain class, methods and attributes in the class through the bytecode file. The realization of reflection mainly uses the following four categories:

Class: Object of the class

Constructor: the construction method of the class

Field: the attribute object in the class

Method: the method object in the class

Role : The reflection mechanism means that the program can obtain its own information while it is running. In Java, as long as the name of the class is given, all the information of the class can be obtained through the reflection mechanism.

The principle of annotation

Annotation is essentially a special interface that inherits Annotation, and its specific implementation class is a dynamic proxy class generated by Java runtime. When we get annotations through reflection, what we return is the dynamic proxy object $Proxy1 generated by the Java runtime. The method of calling a custom annotation (interface) through the proxy object will eventually call the invoke method of AnnotationInvocationHandler. This method will index the corresponding value from the map of memberValues. The source of memberValues ​​is the Java constant pool.

Synchronized and lock

synchronized is a keyword in Java . When it is used to modify a method or a block of code, it can ensure that only one thread executes the code at the same time. After JDK1.5, spin locks, lock coarsening, and lightweight locks have been introduced, and the locks are biased to optimize the performance of keywords.

(1) Lock is an interface , synchronized is a keyword in Java, synchronized is a built-in language implementation;

(2) Synchronized will automatically release the lock held by the thread when an exception occurs, so it will not cause a deadlock . When a Lock occurs, if it does not actively release the lock through unLock(), it is likely to cause a deadlock. Phenomenon, so when using Lock, you need to release the lock in the finally block;

(3) Lock can make the thread waiting for the lock respond to the interrupt, but synchronized does not work. When using synchronized, the waiting thread will wait forever and cannot respond to the interrupt;

(4) Through Lock, you can know whether the lock has been successfully acquired, but synchronized can't do it.

The difference between Integer and int=5:

Java is a nearly pure object-oriented programming language, but for the convenience of programming, basic data types are introduced, but in order to be able to treat these basic data types as object operations, Java introduces corresponding packaging types for each basic data type ( wrapper class). The wrapper class of int is Integer. Since Java 5, an automatic boxing/unboxing mechanism has been introduced to make the two convertible.

Java provides a packaging type for each primitive type:

-Primitive types: boolean, char, byte, short, int, long, float, double

-Packaging types: Boolean, Character, Byte, Short, Integer, Long, Float, Double

Such as:

class AutoUnboxingTest {

public static void main(String[] args) {

Integer a = new Integer(3);

Integer b = 3; // automatically box 3 into Integer type

int c = 3;

System.out.println(a == b); // false Two references do not refer to the same object

System.out.println(a == c); // true a is automatically unboxed into int type and then compared with c



Could you please explain the class loading mechanism, the parent delegation model, what are the benefits?

When a specific class loader receives a request to load a class, it first delegates the loading task to the parent class loader, and then recursively. If the parent class loader can complete the class loading task, it returns successfully; only the parent class loader When the loading task cannot be completed, load it by itself.

The advantage of using the parental delegation model is that the Java class has a priority hierarchical relationship along with its class loader. For example, the class java.lang.Object, which exists in rt.jar, no matter which class loader wants to load this class, it is ultimately delegated to the Bootstrap ClassLoader at the top of the model to load, so the Object class is used in various programs The class loader environment is all the same class. On the contrary, if there is no parent delegation model but loaded by each class loader, if the user writes a java.lang.Object class with the same name and puts it in the ClassPath, there will be multiple different Object classes in the system. The program will be messed up. Therefore, if a developer tries to write a Java class with the same name as the rt.jar library, it can be compiled normally, but it will never be loaded and run.

Java container:

A container is a program written in java. Originally, you had to write your own program to manage object relationships, and the container would do it for you automatically.

Commonly used containers: WebSphere, Weblogic, Resinm, Tomcat, Glassfish

Java container classes include: list, Arraylist, Vector and map, hashTable, HashMap, HashSet

wait and sleep:

Sleep is a method of the thread class (Thread), which causes this thread to suspend execution for a specified time and give the execution opportunity to other threads, but the monitoring state is still maintained and will automatically resume after that time. Calling sleep will not release the object lock.

Wait is a method of the Object class. Calling the wait method on this object causes the thread to give up the object lock and enter the waiting lock pool waiting for this object. Only after the notify method (or notifyAll) is issued for this object, the thread enters the object lock pool and prepares to obtain it. The object lock enters the running state.

The underlying implementation principle of hashSet:

Hashset is unordered and cannot be repeated.

The bottom layer of Hashset uses a hash table (a hash table integrates the advantages of an array and a singly linked list). Characterized by fast storage .

When adding an element to the hashset, the hashset will first call the element's hashcode method to obtain the element's hash value, and then through the XOR or shift operation with the cement element's hash value, the element can be calculated in the hash table Storage location.

Principle of operation:

If the calculated storage location of the element does not currently have any element storage, then the element can be stored directly at that location. If there are already other elements in the calculated storage location of the element, then the equals method of the element will be called , Compare it with the element at the position once, if the equals method returns true, then the element at the position will be regarded as a repeating element and it is not allowed to be added, if false, it is allowed to be added.

Implementation principle :

Hashset is implemented based on hashmap. The default constructor is to build a hashmap with an initial capacity of 16 and a load factor of 0.75. A hashmap object is encapsulated to store all the collection elements, and all the collection elements placed in the hashset are actually stored by the hashmap key.

What is the difference between hashSet and treeset:

Hashset is implemented by a hash table, so its elements are disordered, and the time complexity of the add, remove, and contains methods is O(1)

The treeset is implemented by a tree structure, and the elements in it are ordered. Therefore, the time complexity of the add, remove, and contains methods is O(logn)

Binary tree, polytree, B tree, B+ tree, B* tree

The operation efficiency of the binary tree is high, but there are problems.

  1. The binary tree needs to be loaded into memory. If the binary tree has fewer nodes, there is no problem, but if the binary tree has a lot of nodes (100 million), there will be problems.
  2. Question 1: When building a binary tree, multiple I/O operations are required (massive data is stored in a database or file), and there are a large number of nodes. When building a binary tree, the speed has an impact.
  3. Problem 2: The large number of nodes will also cause the height of the binary tree to be very large, which will reduce the operation speed.

Multitree: In a binary tree, each node has data items, and there are at most two child nodes. If each node is allowed to have more data items and more child nodes, it is a polytree . The multi-tree can optimize the binary tree by reorganizing the nodes to reduce the height of the tree. ( 2-3 trees, 2-3-4 trees)

B- tree (B-tree/B- tree) (a kind of polytree): B- tree reduces the height of the tree by reorganizing nodes, and is widely used in file systems and database systems.

B stands for Balanced, meaning balance.

Features: Store data in both leaf nodes and non-leaf nodes.

Why should the B- tree be set to multiple paths?

Answer: In order to further reduce the height of the tree. But it cannot be designed to be infinitely multi-channel, because if the number of channels is not limited, the B- tree will degenerate into an ordered array, and the file system and database index are stored on the hard disk, and if the amount of data is large, it may not be able to be one-time Load into memory.

B- trees have more indexes for file systems.

B+ tree ( a variant of B tree) (also one more multi-way search tree):

Features: (1 ) All data (keywords) are placed on leaf nodes.

B+ trees do more indexes for databases.

This is related to business scenarios . When selecting data in the database , it is not necessary to select only one item. In many cases, multiple items are selected, for example , 10 items are selected after sorting by id . The use of B- tree requires partial in-order traversal, which may require cross-layer access. In the B+ tree, because all data is in the leaf nodes, there is no need to cross-layer, and because there is a linked list structure, only the head and the end need to be found, and all the data can be extracted through the linked list.

Interview question: B+ tree query time is related to the height of the tree, probably log(n), while using hash storage index, the average query time is O(1), since hash is faster than B+ tree, why mysql still uses How about b+ tree to store index?

Answer: This is related to the business scenario. If only one data is selected, it is indeed faster to hash . However, multiple entries are often selected in the database. At this time, because the B+ tree index is ordered and linked by the linked list, its query efficiency is much faster than hash . And the index in the database is generally on the disk, and the large amount of data may not be able to be loaded into the memory at one time. The design of the B+ tree can allow data to be loaded in batches, and the height of the tree is low, which improves the search efficiency.

B* tree ( a variant of B+ tree) ( add pointers to brothers at the non-root and non-leaf nodes of the B+ tree):

1. How to quickly remove duplicate elements in a list set?

(1) Use HashSet to remove duplicates

package com.ggqq; import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.List; public class Test01 {    public static void main(String[] args) {        List list = new ArrayList();        list.add("123");        list.add("123");        list.add("234");        list.add("789");        /*//遍历        //方法一:for循环        for(int i = 0; i<list.size();i++){            System.out.println(list.get(i));        }        //方法二:iterator迭代器        Iterator iterator = list.iterator();        while(iterator.hasNext()){            System.out.println(;        }*/        //方法三:增强for        for(Object s:list){            System.out.println(s);        }         //list添加到HashSet中去重        HashSet hashSet = new HashSet(list);        //清空list        list.clear();        //将HashSet添加到list中        list.addAll(hashSet);        //遍历        for(int i = 0; i<list.size();i++){            System.out.println(list.get(i));        }    }}

(2) De-duplication through the contains() method of List

package com.ggqq; import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.List; public class Test02 {    public static void main(String[] args) {        List list = new ArrayList();        list.add("123");        list.add("123");        list.add("234");        list.add("789");         //遍历        Iterator iterator = list.iterator();        while(iterator.hasNext()){            System.out.println(;        }        System.out.println("------------------------");        //新建一个tempList,用于存放去重后的        List tempList = new ArrayList();        for(int i = 0 ; i <list.size();i++){            if(!tempList.contains(list.get(i))){                tempList.add(list.get(i));            }        }        //遍历        Iterator iterator2 = tempList.iterator();        while(iterator2.hasNext()){            System.out.println(;        }    }}

(3) Judging through the two-layer for loop

package com.ggqq; import java.util.ArrayList;import java.util.Iterator;import java.util.List; public class Test03 {    public static void main(String[] args) {        List list = new ArrayList();        list.add("123");        list.add("123");        list.add("234");        list.add("789");         //遍历        Iterator iterator = list.iterator();        while(iterator.hasNext()){            System.out.println(;        }        System.out.println("------------------------");        //从list中索引为0开始往后遍历        for(int i = 0 ; i <list.size()-1;i++){            for(int j = list.size()-1; j>i; j-- ){                if(list.get(i).equals(list.get(j))){                    //去重                    list.remove(j);                }            }        }        //遍历        Iterator iterator2 = list.iterator();        while(iterator2.hasNext()){            System.out.println(;        }    }}
2. What is ThreadLocal? What are the usage scenarios?

ThreadLocal is thread local storage. A ThreadLocalMap object is created in each thread, and each thread can access the value in its own internal ThreadLocalMap object.

The classic usage scenario is to allocate a JDBC connection for each thread. In this way, it can be ensured that each thread performs database operations on its own Connection, and there will be no problems with A thread shutting down the Connection being used by B thread; there are also Session management and other issues.

ThreadLocal usage example:

package com.ggqq; public class TestThreadLocal {     //线程本地存储变量    private static final ThreadLocal<Integer> THREAD_LOCAL_NUM = new ThreadLocal<Integer>() {        @Override        protected Integer initialValue() {            return 0;        }    };     public static void main(String[] args) {        for (int i = 0; i < 3; i++) {//启动三个线程            Thread t = new Thread() {                @Override                public void run() {                    add10ByThreadLocal();                }            };            t.start();        }    }     /**     * 线程本地存储变量加 5     */    private static void add10ByThreadLocal() {        for (int i = 0; i < 5; i++) {            Integer n = THREAD_LOCAL_NUM.get();            n += 1;            THREAD_LOCAL_NUM.set(n);            System.out.println(Thread.currentThread().getName() + " : ThreadLocal num=" + n);        }    } }


3. What is a deadlock?

Deadlock refers to a phenomenon in which two or more processes are blocked due to competition for resources or due to communication with each other during the execution. If there is no external force, they will not be able to advance. At this time, the system is said to be in a deadlock state or the system has a deadlock. These processes that are always waiting for each other are called deadlock processes. It is an error at the operating system level, short for process deadlock.

4. Implementation of distributed locks

(1) Why do we need distributed locks?

First of all, we should first understand the usage scenarios of distributed locks, and then understand why distributed locks are needed. Let me give two examples to illustrate:
Application scenarios:
1: Bank transfer problem (this scenario is not easy to explain): A is in Shanghai, and B is in Beijing at the same time in the construction bank to transfer to Hangzhou C, when A transfers money, C will be modified The table of the server, B cannot transfer at this moment, similarly, when B transfers, A cannot do processing, and the transfer operations of A and B are synchronized. Data consistency must be ensured, which requires distributed locks for processing.
2: Take task problem: A service provides a set of tasks. System A requests a task randomly obtained from the task group; System B requests a task randomly obtained from the task group. In an ideal situation, A picks a task from the task group, the task group deletes the task, and B picks another task from the remaining tasks, and the task group deletes the task. Similarly, in a real situation, if nothing is done, A and B may pick the same task.

(2) Why can't ordinary locks be used in distributed systems? So what is the difference between ordinary locks and distributed locks?

Ordinary lock : In a single system, the same application has the same process, and then multiple threads concurrency will cause data security problems. They share the same memory, so mark somewhere in the memory to meet the needs, for example Synchronized is the same as volatile+cas to mark the specific code, which corresponds to the synchronization mark in the same memory area.
Distributed lock : In a distributed system, the biggest difference is that applications in different systems are processed in different processes on their respective machines. The thread insecurity here can be understood as a data security problem caused by multiple processes. They are not Will share the same memory area of ​​the same machine, so the tag needs to be stored in a place that all processes can see. For example, zookeeper is used as a distributed lock, which is to store the lock mark in a common place seen by multiple processes, and redis is used as a distributed lock to mark it in common memory, rather than the area allocated by a certain process.

(3) Three implementation methods of distributed locks

a: Zookeeper implements distributed locks (most used)

Implementation method:
Option 1 : Use the uniqueness of the node name to implement a shared lock.
Algorithm idea: Using the uniqueness of the name, when the lock operation is performed, all clients only need to create the /test/Lock node together, and only one is created successfully, and the successful one obtains the lock. When unlocking, you only need to delete the /test/Lock node, and the remaining clients will enter the competition to create nodes again until all clients obtain the lock.
Option 2 : Use temporary sequence nodes to implement shared locks. (Mainly implemented in this way)
Algorithm idea: For the lock operation, all clients can go to the /lock directory to create a temporary sequence node, if the created client finds that the sequence number of the node created by itself is in the /lock/ directory The smallest node gets the lock. Otherwise, monitor the node with a sequence number smaller than that of the node created by yourself (the largest node smaller than the node created by yourself), and enter the wait.
For example, create nodes: /lock/0000000001, /lock/0000000002, /lock/0000000003. Then the node /lock/0000000001 will obtain the lock first, because the nodes on zk are ordered, and the smallest node obtains the lock first.
Note: The advantage of temporary sequence nodes over persistent sequence nodes is that when zookeeper goes down, the temporary sequence nodes will be automatically deleted, and the client acquiring the lock will release the lock, which will not cause lock waiting all the time, while the persistent node will cause lock waiting.
The difference between the two methods :
Scheme 1 will produce a thundering group effect: if many clients are waiting for a lock, all clients are awakened when the lock is released, and then compete for distributed locks, and only one client gets the lock.
Solution 2 is the implementation of queuing in the order of creation. Multiple clients wait for the lock together. When the lock is released, only one client will be awakened. The client with the smallest registered node on zk will be awakened, avoiding the thrilling group effect.

b: Redis implements distributed locks (the second one is used)

Redis implements distributed locks mainly by four commands:
setnx (set if not exits maintains an optimistic lock): When there is no key, the value is set for the key. The difference between setnx and set: If the key exists in the set, the value is overwritten; if the key does not exist in the setnx, the key and value are re-assigned.
getset: Get the old value according to the key and set the new value.
expire: Set the expiration time.
del: delete

Implementation method:
1: When acquiring the lock, use setnx to add the lock, and use the expire command to add a timeout period to the lock. The lock will be automatically released after this time. The value of the lock is a randomly generated UUID, and the lock is released through this Make judgments at the time.
2: When acquiring the lock, it also sets an acquisition timeout period, if it exceeds this time, it will give up acquiring the lock.
3: When releasing the lock, judge whether it is the lock by UUID, if it is the lock, execute delete to release the lock.

c: The database implements distributed locks (the least used)

Implementation method: optimistic locking and pessimistic locking are used.
Optimistic locking: add the version number field to the table, query the data with the version number before each update, and then update the where condition statement with the version number condition after the statement , Successful update means that the lock is occupied, and unsuccessful update means that the lock is not occupied.
Pessimistic lock: use select...for update (X lock)/select...lock in share mode (S lock). Generally speaking, X locks are used more, because more write functions will be implemented later.
Note: When the pessimistic lock is implemented, the transaction auto-commit mechanism of the database needs to be closed or it will not take effect. Therefore, the java code should choose to actively close the transaction auto-commit function of the database.

5. Talk about the implementation principle of HashMap

Hashset is unordered and cannot be repeated.

The bottom layer of Hashset uses a hash table (a hash table integrates the advantages of an array and a singly linked list). The characteristic is fast storage .

When adding an element to the hashset, the hashset will first call the element's hashcode method to obtain the element's hash value, and then through the XOR or shift operation with the cement element's hash value, the element can be calculated in the hash table Storage location.

Principle of operation:

If the calculated storage location of the element does not currently have any element storage, then the element can be stored directly at that location. If there are already other elements in the calculated storage location of the element, then the equals method of the element will be called , Compare it with the element at the position once, if the equals method returns true, then the element at the position will be regarded as a repeating element and it is not allowed to be added, if false, it is allowed to be added.

Implementation principle :

Hashset is implemented based on hashmap. The default constructor is to build a hashmap with an initial capacity of 16 and a load factor of 0.75. A hashmap object is encapsulated to store all the collection elements, and all the collection elements placed in the hashset are actually stored by the hashmap key.

6. What is the difference between poll() and remove() in Queue?

Both poll() and remove() will remove and return to the head of the queue, but poll() will return null when the queue is empty, and remove() will throw a NoSuchElementException.

7. New features of JDK1.8:
8. Application scenarios of application and bootstrap:

application: The configuration file is easy to understand and is mainly used for the automatic configuration of SpringBoot projects.

bootstrap: The configuration file has the following application scenarios:

  • When using the SpringCloud Config configuration center, you need to add configuration properties connected to the configuration center in the bootstrap configuration file to load the configuration information of the external configuration center;
  • Some fixed attributes that cannot be overridden;
  • Some encryption/decryption scenarios.
application.yml 是用户级别的配置,而bootstrap.yml 是系统级别的配置
9. Can the key and value of HashMap and HashTable be null?

HashMap can store elements whose Key is null and multiple values ​​are null, but Hashtable cannot store them

(Look at the source code)

10. What is Spring?

Spring is a lightweight Java development framework that emerged in 2003. It was created to solve the complexity of enterprise application development. The core of Spring is inversion of control (IOC) and aspect-oriented programming (AOP).

The role of Spring is to decouple the code and reduce the coupling degree of the code. It means that the relationship between objects and objects (modules and modules) is not associated with code, but explained through configuration.

IOC is also called automatic injection. Injection means assignment. The main business of IOC does not need to maintain the relationship when calling each other, that is, you do not need to create the objects to be used by yourself, but are managed by the Spring container.

AOP is the standardization of dynamic agents. AOP is an important part of the Spring framework. The use of AOP can isolate various parts of business logic, thereby reducing the coupling between various parts of business logic and improving the repeatability of the program. At the same time, the efficiency of development is improved.

SpringMVC who tunes who is the problem

In SpringMVC, the use of annotations is strengthened, and annotations can be used in the Controller, Service, and Dao layers.

  • Use @Controller to create a processor object
  • @Service creates business objects
  • @Autowired or @Resource injects Service in the Controller class and Dao in the Service class.

The processor method of the processor annotated with @Controller has four types of return value types commonly used:

The first type: ModelAndView (passing data + passing view (that is, jump to other resources))

The second type: String (view)

The third type: no return value void (processing Ajax)

The fourth type: return custom type object (data)

Distinguish whether the return value String is data or view, see if there is any @ResponseBody annotation, if there is, it is data, otherwise it is view.

2. What is the difference between cookie and session?

Session is a key used by the server to verify user permissions. It is stored on the server and used during data interaction. The login failure scenarios we often encounter are because the session plays a role in the middle.

Cookies are data stored locally ( client side ). You can simply understand that when you enter an account on the page, a password will pop up automatically. This password pops up because of local cookies, including historical records, and there are records. , Because the content is stored in a local cookie file.

  • 1. Since the HTTP protocol is a stateless protocol, when the server needs to record the user's state, it needs to use some mechanism to identify the specific user. This mechanism is Session. A typical scenario such as a shopping cart, when you click to place an order When the button is pressed, because the HTTP protocol is stateless, it is not known which user is operating it, so the server has to create a specific session for a specific user to identify the user and track the user, so that it knows that there is in the shopping cart A few books. This Session is stored on the server and has a unique identifier. There are many ways to save Session on the server, including memory, database, and files. When clustering, you should also consider Session transfer. In large websites, there will usually be dedicated Session server clusters to save user sessions. At this time, Session information is stored in memory and some caching services such as Memcached are used. Come and put Session.
  • 2. Think about how the server recognizes specific customers? At this time Cookie is on the scene. Each time an HTTP request is made, the client will send the corresponding cookie information to the server. In fact, most applications use cookies to implement Session tracking. When a Session is created for the first time, the server will tell the client in the HTTP protocol that a Session ID needs to be recorded in the Cookie. The session ID is sent to the server, and I know who you are. Someone asked, what if cookies are disabled on the client's browser? Generally, in this case, a technology called URL rewriting is used for session tracking, that is, every HTTP interaction, a parameter such as sid=xxxxx will be appended to the URL, and the server will identify the user based on this.
  • 3. Cookies can actually be used in some user-friendly scenarios. Suppose you have logged in to a website one time, and you don't want to enter your account again when you log in next time. What should you do? This information can be written in the Cookie. When visiting the website, the script of the website page can read this information, and it will automatically fill in the user name for you, which can be convenient for users. This is also the origin of the cookie name, giving users a bit of sweetness.
  • So, to summarize:
  1. Session is a data structure saved on the server to track the status of users. This data can be saved in clusters, databases, and files;
  2. Cookie is a mechanism for the client to save user information. It is used to record some user information and is also a way to implement Session.
3. The difference between springmvc and springboot

The Spring framework is like a family, with many derivative products such as boot, security, jpa and so on. But their foundation is that Spring's ioc and aop ioc provide dependency injection container aop, which solves cross-section-oriented programming, and then implements the advanced functions of other extended products on the basis of the two. Spring MVC is a Servlet-based MVC framework that mainly solves the problem of WEB development, because the configuration of Spring is very complicated, and the processing of various XML, JavaConfig, and hin is relatively cumbersome. Therefore, in order to simplify the use of developers, Spring boot was creatively introduced, and the convention is better than the configuration, which simplifies the spring configuration process.

To put it more simply: Spring originally used "factory mode" (DI) and "agent mode" (AOP) to decouple application components. Everyone thinks it's very easy to use, so I built an MVC framework (some components decoupled with Spring) according to this model, and used it to develop web applications (SpringMVC). Then I discovered that every time I wrote a lot of boilerplate code, in order to simplify the work process, so I developed some "lazy man integration package" (starter), this set is Spring Boot.

Features of Spring MVC

Spring MVC provides a lightly coupled way to develop web applications.

Spring MVC is a module of Spring, a web framework. Through Dispatcher Servlet, ModelAndView and View Resolver, it is easy to develop web applications. The problem area to be solved is website application or service development-URL routing, Session, template engine, static web resources, etc.

Features of Spring Boot

Spring Boot implements automatic configuration and reduces the complexity of project construction.

As we all know that the Spring framework requires a lot of configuration, Spring Boot introduces the concept of automatic configuration to make project settings easy. Spring Boot itself does not provide the core features and extended functions of the Spring framework, but is only used to quickly and agilely develop a new generation of applications based on the Spring framework. In other words, it is not a solution to replace Spring, but a tool that is closely integrated with the Spring framework to enhance the Spring developer experience. At the same time, it integrates a large number of commonly used third-party library configurations (such as Jackson, JDBC, Mongo, Redis, Mail, etc.). These third-party libraries in Spring Boot applications can be used out-of-the- box), most Spring Boot applications require only a very small amount of configuration code, and developers can focus more on business logic.

Spring Boot is just the carrier, helping you to simplify the project construction process. If you are carrying a WEB project and using Spring MVC as the MVC framework, then the workflow is exactly the same as you described above, because this part of the work is done by Spring MVC instead of Spring Boot.

For users, after switching to Spring Boot, the project initialization method has changed and the configuration file has changed. In addition, there is no need to install a container server such as Tomcat separately. There is no change between the core business logic implementation and the business process implementation.

So, to summarize in the most concise language is:

Spring is an "engine";

Spring MVC is an MVC framework based on Spring;

Spring Boot is a set of rapid development integration package based on Spring4 conditional registration.

5. What are the microservice technology stacks you know? List one or two
  • Service development: springboot spring springmvc
  • Service configuration and management: Archaiusm of Netfix, Diamond of Ali
  • Service registration and discovery: Eureka, Zookeeper
  • Service call: Rest RPC gRpc
  • Service fuse: Hystrix
  • Service load balancing: Ribbon Nginx
  • Service interface call: Fegin
  • Message queue: Kafka Rabbitmq activemq
  • Service Configuration Center Management: SpringCloudConfig
  • Service routing (API gateway) Zuul
  • Event message bus: SpringCloud Bus
Microservice technology entryLanding technology
Service developmentSpringBoot, Spring, SpringMVC, etc.
Service configuration and managementArchaius of Netfix, Diamond of Ali, etc.
Service registration and discoveryEureka, Consul, Zookeeper, etc.
Service callRest, PRC, gRPC
Service fuseHystrix, Envoy, etc.
Load balancingRibbon, Nginx, etc.
Service interface call (a simplified tool for the client to call the service)Fegin etc.
message queueKafka, RabbitMQ, ActiveMQ, etc.
Service Configuration Center ManagementSpringCloudConfig, Chef, etc.
Service routing (API gateway)Zuul et al
Service monitoringZabbix, Nagios, Metrics, Specatator, etc.
Full link trackingZipkin, Brave, Dapper, etc.
Data flow operation development kitSpringCloud Stream (encapsulate and send and receive messages with Redis, Rabbit, Kafka, etc.)
Time message total stackSpringCloud Bus
Service deploymentDocker, OpenStack, Kubernetes, etc.
6. What are the differences between SpringCloud and Dubbo?

compare results:

Service RegistryZookeeperSpring Cloud Netfilx Eureka
Service invocation methodRPCREST API
Service monitoringDubbo-monitorSpring Boot Admin
breakerimperfectSpring Cloud Netfilx Hystrix
Service gatewaynoSpring Cloud Netfilx Zuul
Distributed configurationnoSpring Cloud Config
Service trackingnoSpring Cloud Sleuth
Total message stacknoSpring Cloud Bus
data flownoSpring Cloud Stream
Batch tasknoSpring Cloud Task

The biggest difference : SpringCloud abandoned Dubbb's RPC communication and adopted the REST method of Http

Comparison of Rest and RPC : Strictly speaking, these two methods have their own advantages and disadvantages. Although to a certain extent, Rest sacrifices the performance of service calls, but it also avoids the problems caused by native RPC. Moreover, REST is better than RPC. In order to be flexible, the service provider and the caller only rely on a single contract, and there is no strong dependency at the code level. This point is more appropriate in the current microservice environment that emphasizes rapid evolution.

Comparison of document quality and community activity: SpringCloud community activity is much higher than Dubbo. After all, due to Liang Fei’s team, Dubbo stopped updating and iterating for five years, and small and medium-sized companies could not afford the cost of technology development, resulting in a serious decline in the Dubbo community, while SpringCloud The sudden emergence of a new force has quickly occupied the
microservice market. Backed by the spring mix, Dubbo has been quite mature after years of accumulated documents, and has a stable status quo for the microservice architecture system of various companies.

The difference between a brand machine and an assembly machine: Dubbo is an assembly machine, and SpringCloud is a brand machine

Summary: The problem areas that the two solve are different. Dubbo is positioned as an RPC framework, while the goal of SpringCloud is a one-stop solution under the microservice architecture.

7. SpringBoot and SpringCloud, please talk about your understanding of them
  • SpringBoot focuses on the rapid and convenient development of a single individual microservice (jar package)
  • SpringCloud is a global microservice coordination and management framework. It integrates and manages the individual microservices developed by SpringBoot to provide various microservices: configuration management, service discovery, circuit breakers, routing, and proxy , Event total stack, global lock, decision-making campaign, distributed conversation and other integrated services.
  • SpringBoot can be used independently without SpringCloud to develop projects, but SpringCloud is inseparable from SpringBoot and belongs to a dependency relationship
  • SpringBoot focuses on the rapid and convenient development of individual individual microservices, and SpringCloud focuses on the global microservice governance framework
8. Both Eureka and Zookeeper can provide service registration and discovery functions, please tell us the difference between the two

Review the CAP principle

RDBMS (MySQL\Oracle\sqlServer) ===> ACID

NoSQL (Redis\MongoDB) ===> CAP

What is ACID? (The nature of databases such as orcal mysql)

  • A (Atomicity) atomicity
  • C (Consistency) consistency
  • I (Isolation) isolation
  • D (Durability)

What is CAP?

  • C (Consistency) strong consistency (guarantee data consistency)
  • A (Availability)
  • P (Partition tolerance) Partition tolerance

CAP's three-in-two: either CA, or AP, or CP (there is no guarantee that all three properties are satisfied)

The core of the CAP principle

  • A distributed system cannot satisfy the three requirements of consistency, availability and partition fault tolerance at the same time.
  • According to the CAP principle, the NoSQL database is divided into three categories: satisfying the CA principle, satisfying the CP principle and satisfying the AP principle
  • CA: Single-point cluster, a system that satisfies consistency and availability, and usually has poor scalability
  • CP: a system that satisfies consistency and partition fault tolerance, usually the performance is not particularly high
  • AP: A system that satisfies availability and partition fault tolerance. Generally, it may have lower requirements for consistency

As a distributed service registry, how is Eureka better than Zookeeper?

The well-known CAP theory points out that a distributed system cannot satisfy C (consistency), A (availability), and P (fault tolerance) at the same time. Since partition fault tolerance P must be guaranteed in a distributed system, we only We can make trade-offs between A and C.

  • Zookeeper guarantees that CP —> a system that meets consistency and partition fault tolerance, usually the performance is not particularly high
  • What Eureka guarantees is AP —> a system that meets availability and partition fault tolerance, which usually has lower consistency requirements

Zookeeper guarantees CP (consistency, fault tolerance, poor availability)

​ When querying the registry for the list of services, we can tolerate that the registry returns the registration information a few minutes ago, but the service cannot be received directly down and unavailable. In other words, the service registration function has higher requirements for availability than consistency. But zookeeper will have such a situation, when the master node loses contact with other nodes due to a network failure, the remaining nodes will re-elect the leader. The problem is that the leader election time is too long, 30-120s, and the entire zookeeper cluster is unavailable during the election, which leads to the paralysis of the registration service during the election. In a cloud deployment environment, it is a relatively probable event that the zookeeper cluster loses the master node due to network problems. Although the service can eventually be restored, the long election time causes the registration to be unavailable for a long time, which is intolerable.

Eureka guarantees AP (availability, fault tolerance, poor consistency)

​ Eureka understands this, so we prioritize usability when designing. Each node of Eureka is equal, and the failure of several nodes will not affect the work of normal nodes, and the remaining nodes can still provide registration and query services. When an Eureka client registers with an Eureka, if it finds that the connection fails, it will automatically switch to another node. As long as one Eureka is still there, the availability of the registration service can be maintained, but the information found may not be the latest Yes, in addition, Eureka also has a self-protection mechanism. If more than 85% of the nodes do not have a normal heartbeat within 15 minutes, then Eureka believes that there is a network failure between the client and the registry, and it will appear at this time The following situations:

  • Eureka will not remove from the registration list services that should expire because they have not received heartbeats for a long time
  • Eureka can still accept new service registration and query requests, but it will not be synchronized to other nodes (that is, to ensure that the current node is still available)
  • When the network is stable, the new registration information of the current instance will be synchronized to other nodes

Therefore, Eureka can deal with the situation that some nodes lose contact due to network failures, without paralyzing the entire registration service like zookeeper

7. What is the division and switching of user mode and kernel mode? ?

1. Switching method
There are three ways to switch from user mode to kernel mode, or the operation that will cause switching from user mode to kernel mode:

  • System call: System call itself is interrupt, but software interrupt is different from hard interrupt. The system call mechanism is realized by using an interrupt which is specially opened by the operating system for users, such as the int 80h interrupt of Linux.
  • Exception: If the current process is running in user mode, if an abnormal event occurs at this time, it will trigger the switch from the current running process to the kernel-related process that handles the exception
  • Peripheral device interrupt: After the peripheral device completes the operation requested by the user, it will send an interrupt signal to the CPU, and then the CPU will transfer to the corresponding interrupt handler.

2. What is the cost?
When switching from user mode to kernel mode occurs, the following process will occur (essentially switching from "user program" to "kernel program")

  • Set the processor to kernel mode.
  • Save the current registers (stack pointer, program counter, general-purpose registers).
  • Set the stack pointer to point to the kernel stack address.
  • Set the program counter to a pre-appointed address, which stores the starting address of the system call handler.

And then when returning from the kernel mode to the user mode, similar work will be performed.

3. How to avoid frequent switching. Switching between
user mode and kernel mode has a certain overhead. Frequent switching will inevitably bring a lot of overhead, so we must do everything possible to reduce switching. This is also a common question in interviews.

3.1 Reduce thread switching

Because thread switching will result in switching between user mode and kernel mode, reducing thread switching will also reduce switching between user mode and kernel mode. So how to reduce thread switching?

  • Lock-free concurrent programming. When multiple threads compete for locks, locking and releasing locks will cause more context switches.
Why do locking and releasing locks cause context switching
Synchronized is achieved through a monitor lock (monitor) inside the object. But the essence of the monitor lock is realized by the Mutex Lock of the underlying operating system. However, because the use of Mutex Lock needs to suspend the current thread and switch from user mode to kernel mode for execution, the cost of this switch is very expensive. Therefore, this kind of lock that depends on the operating system Mutex Lock is called " Heavyweight lock".
  • CAS algorithm. Use CAS to avoid locking and blocking threads
  • Use the least number of threads. Avoid creating unnecessary threads
  • Coroutine. Realize multi-task scheduling in a single thread, and maintain switching between multiple tasks in a single thread

3..2 An interview question

How to solve the frequent I/O switching between kernel mode and user mode?

First of all, we must agree with the statement that I/O will cause system calls, which will lead to a switch between kernel mode and user mode . Because the operation of the I/O device occurs in the kernel mode. How to reduce system calls caused by I/O? The answer is: make the user process buffer . Explain the reason below

User process buffer

You see, when some programs read a file, they first apply for a memory array called buffer, and then call read each time to read data with a set byte length and write to the buffer. The subsequent programs all get data from the buffer, and when the buffer is used up, the next call is made to fill the buffer. So: the purpose of the user buffer is to reduce the number of system calls, thereby reducing the time it takes for the operating system to switch between user mode and core mode . In addition to designing buffers in the process, the kernel also has its own buffers.

Kernel buffer

When a user process wants to read data from the disk, the kernel generally does not read the disk directly, but copies the data in the kernel buffer to the process buffer. But if there is no data in the kernel buffer, the kernel will add the request for the data block to the request queue, and then suspend the process to provide services for other processes. When the data has been read into the kernel buffer, the data in the kernel buffer is read into the user process before the process is notified. Of course, different IO models have different ways of scheduling and using the kernel buffer.


The read, write and sync in the figure are all system calls. read is to copy data from the kernel buffer to the process buffer. write is to copy the process buffer to the kernel buffer. Of course, write does not necessarily cause the kernel's cache synchronization action to sync. For example, the OS may accumulate the data in the kernel buffer to a certain amount and then synchronize it to the disk at one time. This is why power failures sometimes lead to data loss. So the kernel buffer can improve disk IO efficiency and optimize disk write operations at the OS level.

11. About the time complexity of arrays
algorithmtime complexity
Linear searchO(N)
Binary searchO(logN)
Insertion of unordered arrayO(1)
Insertion of ordered arrayO(N)
Deletion of unordered arrayO(N)
Deletion of ordered arrayO(N)
12. Four ways of thread start: