This article has been included in my open source JavaGuide: github.com/Snailclimb (【Java Learning + Interview Guide 】 covers most Java programmers need to master the core knowledge) if you think it is good, you can click the Star, encourage!

Why is there only value passing in Java?

Let’s first review some of the jargon in programming languages for passing parameters to methods (or functions). A call by value means that a method receives a value provided by the caller, while a call by Reference means that a method receives a variable address provided by the caller. A method can modify the value of the variable corresponding to the pass-reference, but not the variable corresponding to the pass-value call. It is used to describe how method parameters are passed in various programming languages, not just Java.

The Java programming language always calls by value. That is, the method gets a copy of all the parameter values, which means that the method cannot modify the contents of any parameter variables passed to it.

Here are three examples to illustrate

example 1

public static void main(String[] args) {
    int num1 = 10;
    int num2 = 20;

    swap(num1, num2);

    System.out.println("num1 = " + num1);
    System.out.println("num2 = " + num2);
}

public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;

    System.out.println("a = " + a);
    System.out.println("b = " + b);
}Copy the code

Results:

a = 20
b = 10
num1 = 10
num2 = 20Copy the code

Resolution:

In the swap method, the values of A and B are exchanged, and num1 and NUM2 are not affected. Because the values in a and b are only copied from num1 and num2. In other words, A and B are equivalent to copies of NUM1 and num2, and no matter how the contents of the copies are modified, the original will not be affected.

In the example above, we already know that a method cannot modify an argument of a basic data type, unlike an object reference as an argument, as shown in example2.

example 2

public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; System.out.println(arr[0]); change(arr); System.out.println(arr[0]); } public static void change(int[] array) {array[0] = 0; }Copy the code

Results:

1
0Copy the code

Resolution:

A copy of the arR in which the array is initialized is also a reference to an object, which means that array and ARR refer to the same array object. Therefore, external changes to referenced objects are reflected on the corresponding objects.

As we have seen in example2, it is not difficult to implement a method to change the state of an object parameter. The reason is simple: the method gets a copy of the object reference, and the object reference and other copies reference the same object at the same time.

Many programming languages (in particular, C++ and Pascal) provide two ways to pass parameters: value calls and reference calls. Some programmers (and even the author of this book) think that the Java programming language calls objects by reference. In fact, this is not true. Because of the universality of this misconception, a counter example is given below to elaborate on the problem.

example 3

public class Test { public static void main(String[] args) { // TODO Auto-generated method stub Student s1 = new Student (" zhang "); Student s2 = new Student(" s2 "); Test.swap(s1, s2); System.out.println("s1:" + s1.getName()); System.out.println("s2:" + s2.getName()); } public static void swap(Student x, Student y) { Student temp = x; x = y; y = temp; System.out.println("x:" + x.getName()); System.out.println("y:" + y.getName()); }}Copy the code

Results:

X: Li Y: Xiao Zhang S1: XIAO Zhang S2: Xiao LiCopy the code

Resolution:

Before the exchange:

After the exchange:

It is clear from the above two figures that the method does not change the object reference stored in the variables S1 and S2. The arguments x and y to swap are initialized as copies of the two object references, and this method swaps the two copies

conclusion

The Java programming language does not invoke objects by reference; in fact, object references are passed by value.

Let’s summarize the use of method parameters in Java:

  • A method cannot modify parameters of a basic data type (that is, numeric or Boolean).
  • A method can change the state of an object parameter.
  • A method cannot have an object parameter reference a new object.

Reference:

“Java Core Technology Volume ⅰ” basic knowledge 10th edition chapter 4 section 4.5

== = equals(important)

== : Determines whether the addresses of two objects are equal. That is, determine whether two objects are the same object. (The basic datatype == compares values, and the reference datatype == compares memory addresses)

Equals () : determines whether two objects are equal. But it can be used in two ways:

  • Case 1: Class does not override the equals() method. Comparing two objects of this class via equals() is equivalent to comparing the two objects via “==”.
  • Case 2: Class overrides the equals() method. In general, we override equals() so that the contents of two objects are equal; Return true if their contents are equal (that is, the objects are considered equal).

Here’s an example:

public class test1 { public static void main(String[] args) { String a = new String("ab"); B = new String("ab"); String aa = "ab"; String aa = "ab"; String bb = "ab"; If (aa ==bb) // true system.out.println ("aa==bb"); If (a ==b) // false, not the same object system.out.println ("a==b"); if (a.equals(b)) // true System.out.println("aEQb"); If (42 == 42.0) {// true system.out.println ("true"); }}}Copy the code

Description:

  • The equals method in String is overridden because the equals method of object compares the memory address of the object, while the equals method of String compares the value of the object.
  • When an object of type String is created, the virtual machine looks in the constant pool for an existing object with the same value as the one being created, and if so, assigns it to the current reference. If not, create a new String in the constant pool.

Three hashCode vs. equals (important)

The interviewer may ask you, “Have you ever overridden hashcode and equals, and why do you have to override hashcode when overriding equals?”

3.1 Introduction to hashCode ()

The function of hashCode() is to get the hashCode, also known as a hashCode; It actually returns an int. The function of this hash code is to determine the index position of the object in the hash table. HashCode () is defined in the JDK’s object.java, which means that any class in Java contains a hashCode() function. Note also that the Object hashcode method is a local method, that is, implemented in C or C ++. This method is usually used to convert the Object’s memory address to an integer and return it.

    /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java™ programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();Copy the code

A hash table stores key-value pairs. The characteristic of a hash table is that it can quickly retrieve the corresponding value based on the key. Hash code is used in this! (Can quickly find the object you need)

3.2 Why does hashCode exist

Let’s use “How a HashSet checks for duplications” as an example of why we have hashcodes:

When you add an object to a HashSet, the HashSet first evaluates the object’s Hashcode value to determine where it was added, and also compares it to the hashcode values of other objects that have already been added. If there is no matching Hashcode, A HashSet assumes that the object is not repeated. However, if objects with the same Hashcode value are found, the equals () method is called to check if the objects with the same Hashcode are really the same. If they are the same, the HashSet will not allow them to be added successfully. If not, it will be rehashed to another location. (From my Java primer, Head Fist Java, 2nd edition). In this way, we greatly reduce the number of equals times, which in turn greatly improves the execution speed.

3.3 hashCode () and equals ()

  1. If the two objects are equal, the Hashcode must also be the same
  2. Two objects are equal, so calling equals on both objects returns true
  3. Two objects have the same Hashcode value, and they are not necessarily equal
  4. Therefore, if the equals method is overridden, the hashCode method must also be overridden
  5. The default behavior of hashCode() is to generate unique values for objects on the heap. If hashCode() is not overridden, the two objects of the class will not be equal in any way (even if the two objects point to the same data)

3.4 Why do two objects have the same HashCode value and are not necessarily equal?

Here explains a friend’s problem. The following is an excerpt from Head Fisrt Java.

Because the hash algorithm used by hashCode() might just cause multiple objects to return the same hash value. The worse the hash algorithm, the more likely it is to collide, but this also has to do with the nature of the data-range distribution (collisions mean that different objects get the same hashCode).

We just mentioned hashsets. If a HashSet compares multiple objects in the same Hashcode, it uses equals() to determine if they are the same. In other words, hashcode is simply used to reduce the cost of finding.

What is the difference between String and StringBuffer and StringBuilder? Why is String immutable?

variability

To put it simply: The String class uses the final keyword to decorate an array of characters to hold the String, private final char value[], so the String object is immutable. And both StringBuilder and StringBuffer inherit from AbstractStringBuilder, AbstractStringBuilder also uses an array of characters to hold the string char[]value but is not modified with the final keyword, so both objects are mutable.

The constructor for StringBuilder and StringBuffer is implemented by calling the parent constructor, which is AbstractStringBuilder. You can consult the source code.

AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    AbstractStringBuilder() {
    }
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }Copy the code

Thread safety

Objects in a String are immutable, and thus can be understood as constant and thread-safe. AbstractStringBuilder is a public superclass of StringBuilder and StringBuffer. It defines some basic string operations, such as expandCapacity, Append, INSERT, indexOf and other public methods. StringBuffer synchronizes the method or the method being called, so it is thread-safe. StringBuilder does not synchronously lock methods, so it is non-thread-safe.

performance

Each time a String is changed, a new String is generated, and the pointer is pointed to the new String. Instead of generating new objects and changing object references, StringBuffer operates on the StringBuffer object itself each time. Using StringBuilder is only 10% to 15% better than using StringBuffer, but at the risk of multithreading insecurity.

Summary of the use of the three:

  1. Operate on small amounts of data: Applies to strings
  2. Single-threaded operation Manipulates large amounts of data within a string buffer: Applies to StringBuilder
  3. Multithreaded operations operate on large amounts of data under a StringBuffer: StringBuffer applies

Why is String immutable?

The String class uses final-modified char arrays to store characters.

    /** The value is used for character storage. */
    private final char value[];Copy the code

Is String really immutable?

I think if someone asks that question, the answer should be immutable. Here are just two typical examples:

1) Strings are immutable, but that does not mean references are immutable

        String str = "Hello";
        str = str + " World";
        System.out.println("str=" + str);Copy the code

Results:

str=Hello WorldCopy the code

Resolution:

In fact, the contents of the original String are the same, but instead of pointing to the memory address of “Hello”, STR is pointing to the memory address of “Hello World”, which means that there is an extra area of memory for the “Hello World” String.

2) So-called “immutable” objects can be modified by reflection

// Create the String "Hello World" and assign it to the reference s String s = "Hello World"; System.out.println("s = " + s); / / Hello World / / get the String class in the value Field Field valueFieldOfString = String. Class. GetDeclaredField (" value "); / / change the value attribute access valueFieldOfString setAccessible (true); Char [] value = (char[]) valueFieldofString.get (s); // Change the 5th character in the array referenced by value[5] = '_'; System.out.println("s = " + s); // Hello_WorldCopy the code

Results:

s = Hello World
s = Hello_WorldCopy the code

Resolution:

Using reflection, you can access private members and then reflect the value property of the String, thereby changing the structure of the array by obtaining a value reference. But normally we don’t do that, but just to mention that there’s this thing.

What is the reflex mechanism? What are the application scenarios of the reflection mechanism?

5.1 Reflection Mechanism

JAVA reflection mechanism is in the running state, for any class, can know all the properties and methods of that class; You can call any method or property of any object. This ability to dynamically retrieve information and dynamically invoke methods on objects is called the Reflection mechanism of the Java language.

5.2 Static and dynamic compilation

  • Static compilation: Types are determined at compile time and objects are bound
  • Dynamic compilation: the type is determined at runtime and the object is bound

5.3 Advantages and Disadvantages of the Reflection Mechanism

  • Advantages: runtime type judgment, dynamic class loading, improved code flexibility.
  • Cons: Performance bottleneck: Reflection acts like a series of interpreted actions that tell the JVM what to do, and performance is much slower than direct Java code.

5.4 Application Scenarios of Reflection

Reflection is the soul of frame design.

In our usual project development process, the reflection mechanism is rarely directly used, but this does not mean that the reflection mechanism is useless, in fact, there are a lot of design and development are related to the reflection mechanism, such as modular development, through reflection to call the corresponding bytecode; Reflection mechanism is also used in dynamic proxy design pattern, as well as in Spring/Hibernate and other frameworks that we use every day.

For example: (1) we use class.forname () to load the database driver through reflection when we connect to the database using JDBC; ②Spring framework also uses a lot of reflection mechanism, the most classic is the XML configuration mode. The process of Spring loading beans through XML configuration mode is as follows: 1) load all XML or Properties configuration files in the program into memory; 2) The Java class parses the content in XML or Properties to get the bytecode string and related attribute information of the corresponding entity class; 3) Use reflection to get a Class instance of a Class based on this string; 4) Dynamically configure instance properties

Recommended Reading:

  • Reflection: Application scenarios of the Java Reflection mechanism
  • Java Basics – Reflection (very important)

What is JDK? What is JRE? What is the JVM? The connection and difference between the three

6.1 the JVM

The Java Virtual Machine (JVM) is a virtual machine that runs Java bytecode. The JVM has specific implementations for different systems (Windows, Linux, macOS) that aim to use the same bytecode, and they all give the same results.

What is bytecode? What are the benefits of adopting bytecode?

In Java, code understood by the JVM is called bytecode (that is, a file with a.class extension), which is not processor-specific, but virtual machine oriented. Java language solves the problem of low execution efficiency of traditional interpreted languages by bytecode to a certain extent, while retaining the portability of interpreted languages. So Java programs run more efficiently, and because the bytecode is not specific to a particular machine, Java programs can run on computers with many different operating systems without having to be recompiled.

Java programs from source code to run generally have the following 3 steps:

We need to pay special attention to the.class-> machine code step. This is where the JVM class loader first loads the bytecode file and then executes it line by line through the interpreter, which is relatively slow. Also, there are methods and blocks of code that need to be called frequently (known as hot code), so the JIT compiler was introduced later, and the JIT is a run-time compilation. When the JIT compiler completes the first compilation, it saves the machine code corresponding to the bytecode for direct use next time. We know that machine code is definitely more efficient than the Java interpreter. This explains why we often refer to Java as a compile and interpret language.

HotSpot uses Lazy Evaluation. According to the 80-20 rule of thumb, only a small portion of the code (hot code) consumes most of the system’s resources, which is what the JIT needs to compile. The JVM gathers information about how the code is executed each time and makes some optimizations accordingly, so the more times it executes, the faster it gets. JDK 9 introduced a new Compilation mode, AOT(Ahead of Time Compilation), which compilers bytecode directly to machine code, thus avoiding all the overhead of JIT warm-up. The JDK supports layered compilation and AOT collaboration. However, the AOT compiler is certainly not as good as the JIT compiler.

Conclusion:

The Java Virtual Machine (JVM) is a virtual machine that runs Java bytecode. The JVM has specific implementations for different systems (Windows, Linux, macOS) that aim to use the same bytecode, and they all give the same results. Bytecode and JVM implementations for different systems are the key to the Java language’s “compile once, run anywhere” approach.

6.2 the JDK and JRE

The JDK is the Java Development Kit, which is a full-featured Java SDK. It has everything a JRE has, plus a compiler (Javac) and tools (such as Javadoc and JDB). It creates and compiles programs.

JRE is the Java runtime environment. It is the collection of everything you need to run a compiled Java program, including the Java Virtual Machine (JVM), Java class libraries, Java commands, and other building blocks. However, it cannot be used to create new programs.

If you only want to run Java programs, you only need to install the JRE. If you need to do some Java programming, you’ll need to install the JDK. However, this is not absolute. Sometimes, even if you don’t plan on doing any Java development on your computer, you still need to install the JDK. For example, if you are deploying a Web application using JSPS, you are technically just running the Java program in the application server. So why do you need a JDK? This is because the application server converts JSPS into Java servlets, and the JDK is required to compile the servlets.

Chapter VII What is bytecode? What are the biggest benefits of adopting bytecode?

Let’s take a look at the compiler and interpreter in Java:

Java introduced the concept of the virtual machine, a virtual machine with a layer of abstraction between the machine and the compiler. This virtual machine provides a common interface to the compiler on any platform. The compiler simply needs to face the virtual machine, generate code that the virtual machine can understand, and then the interpreter converts the virtual machine code into machine code for execution on a particular system. In Java, this code for virtual machines to understand is called bytecode (files with.class extensions) and is not processor-specific, only virtual machines. The interpreters are different for each platform, but the implemented virtual machines are the same. Java source programs are compiled by the compiler into bytecode, which is interpreted and executed by the virtual machine. The virtual machine sends each piece of bytecode to be executed to the interpreter, which translates it into machine code on a specific machine, and then runs it on a specific machine. This explains the coexistence of compilation and interpretation in Java.

Java source code — — — — — — — — > > compiler JVM executable Java bytecode (that is, the virtual instruction) — — — — — — — — > > JVM JVM interpreter — — — — — > machine executable binary machine code — — — — > program is running.

Benefits of bytecode:

Java language solves the problem of low execution efficiency of traditional interpreted languages by bytecode to a certain extent, while retaining the portability of interpreted languages. So Java programs run more efficiently, and since bytecode is not specific to a particular machine, Java programs can run on many different machines without recompiling.

What is the difference between interfaces and abstract classes?

  1. The default method of an interface is public. All methods cannot be implemented in the interface. Abstract classes can have non-abstract methods
  2. Instance variables in interfaces are final by default, but not necessarily in abstract classes
  3. A class can implement multiple interfaces, but only one abstract class at most
  4. A class that implements an interface implements all the methods of the interface, whereas an abstract class does not
  5. An interface cannot be instantiated with new, but it can be declared, but it must refer to an object that implements the interface. At the design level, an abstraction is an abstraction of a class, it’s a template design, and an interface is an abstraction of behavior, it’s a specification of behavior.

Note: Java8 interfaces can have default implementations.

The difference between overloading and overwriting

overloading

In the same class, the method name must be the same, the parameter type can be different, the number can be different, the order can be different, the method return value and the access modifier can be different.

Here’s how Java Core Technologies introduces the concept of reloading:

rewrite

In a subclass, the method name and parameter list must be the same, the return value range must be less than or equal to the superclass, the exception range must be less than or equal to the superclass, and the access modifier range must be greater than or equal to the superclass. In addition, if the parent method access modifier is private, a child class cannot override the method. That is, the behavior that the method provides changes, but the appearance of the method does not change.

Ten. Java object-oriented programming three features: encapsulation of inheritance polymorphism

encapsulation

Encapsulation privatizes the properties of an object and provides methods that can be accessed by the outside world. If the properties don’t want to be accessed by the outside world, we don’t need to provide methods to the outside world. But if a class does not provide methods for the outside world to access, then the class is meaningless.

inheritance

Inheritance is the technique of using the definition of an existing class as the foundation to create a new class. The definition of the new class can add new data or new functions, or use the functions of the parent class, but it cannot selectively inherit from the parent class. Using inheritance makes it very easy to reuse previous code.

Here are three things to remember about inheritance:

  1. A subclass owns all the properties and methods (including private properties and methods) of a superclass object, but the private properties and methods of the superclass are inaccessible to the subclass.
  2. Subclasses can have their own properties and methods, that is, subclasses can extend the superclass.
  3. A subclass may implement the methods of its parent class in its own way. (later).

polymorphism

So-called polymorphism is refers to the procedures defined in the reference variable is pointing to the specific type and referenced by the variable from method calls are not sure when programming, but during the program is run to determine, that is, a reference variable will point to which class instance of the object, the reference variable from method calls the method to realize exactly is which class, It must be determined during the run of the program.

Polymorphism can be implemented in Java in two forms: inheritance (overwriting of the same method by multiple subclasses) and interface (implementing the interface and overriding the same method in the interface).

What are threads and processes?

11.1 What is a Process?

Process is a program execution process, is the basic unit of the system to run the program, so the process is dynamic. System run a program is a process from the creation, run to the death of the process.

In Java, when we start the main function, we start a JVM process, and the thread in which the main function is located is one of the threads in this process, also known as the main thread.

As shown in the figure below, we can clearly see the process running in Windows (the running of.exe file) by viewing the task manager.

11.2 What is a Thread?

A thread is similar to a process, but a thread is a smaller unit of execution than a process. A process can generate multiple threads during its execution. Unlike the process heap of similar process are Shared by multiple threads and method of area resources, but each thread has its own program counter, the virtual machine and the local method stack, so the system in one thread, or switch between individual threads, burden is much smaller than the process, also because of this, thread, also known as a lightweight process.

Java programs are inherently multithreaded programs. We can use JMX to see what threads a normal Java program has. The code is as follows.

Public class MultiThread {public static void main(String[] args) {// Get the Java thread to manage MXBean ThreadMXBean ThreadMXBean = ManagementFactory.getThreadMXBean(); // Do not need to get the synchronized monitor and synchronizer information, Only for thread and thread stack information ThreadInfo [] threadInfos = threadMXBean. DumpAllThreads (false, false); For (ThreadInfo ThreadInfo: threadInfos) { System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName()); }}}Copy the code

The output of the above program is as follows (it may be different, but don’t worry too much about what each thread does, except that the main thread executes the main method) :

[5] Attach Listener // Add event [4] Signal Dispatcher // Assign thread that processes Signal to JVM [3] Finalizer // Thread that calls object Finalize method [2] Reference Handler // Clear reference thread [1] main // Main thread, program entryCopy the code

As you can see from the output above, a Java program runs with the main thread and multiple other threads running simultaneously.

Please briefly describe the relationship between threads and processes, their differences, advantages and disadvantages.

The relationship between processes and threads from the PERSPECTIVE of the JVM

12.1 Illustrate the relationship between processes and threads

The following figure shows the Java memory region, using the following figure to look at the relationship between threads and processes from the PERSPECTIVE of the JVM. If you don’t know much about Java memory areas (runtime data areas), you can read this article: “Probably the clearest article on Java memory areas.”



As you can see from the figure above, a process can have multiple threads, which share the process’s heap and method area (the metadata after JDK1.8), but each thread has its own program counter, virtual machine stack, and local method stack.

Bottom line: Threads are smaller running units of processes. The main difference between threads and processes is that processes are essentially independent, whereas threads are not, because threads in the same process are more likely to influence each other. Thread execution cost is small, but not conducive to resource management and protection; The reverse is true

The following is an extension of the knowledge!

Consider this question: Why are program counters, virtual machine stacks, and local method stacks thread-private? Why are the heap and method areas shared by threads?

12.2 Why are program counters private?

Program counters have the following two main functions:

  1. Bytecode interpreter changes the program counter to read instructions in turn, so as to achieve code flow control, such as: sequential execution, selection, loop, exception handling.
  2. In the case of multiple threads, a program counter is used to record where the current thread is executing, so that when the thread is switched back, it knows where it was last running.

Note that if a native method is executed, then the program counter records the undefined address. Only when Java code is executed does the program counter record the address of the next instruction.

Therefore, program counters are private mainly so that threads can be restored to the correct execution location after a switch.

12.3 Why are VM stacks and Local Method Stacks Private?

  • Virtual machine stack: Each Java method creates a stack frame to store information such as local variables, operand stack, constant pool references, and so on. The process from the method call to the completion of execution corresponds to the process of a stack frame being pushed and pushed out of the Java virtual machine stack.
  • Local method stack: This function is very similar to that of the virtual machine stack, except that the virtual machine stack performs Java method (bytecode) services for the virtual machine, while the local method stack serves the Native method used by the virtual machine. The HotSpot virtual machine and the Java virtual machine stack are combined.

Therefore, to ensure that local variables in a thread are not accessed by other threads, the virtual machine stack and the local method stack are thread-private.

12.4 Understand the heap and method area in one sentence

The heap and method area are shared resources by all threads. The heap is the largest chunk of memory in the process, mainly used to store newly created objects (all objects are allocated memory here). The method area is mainly used to store loaded class information, constants, static variables, real-time compiler compiled code and other data.

What is the difference between concurrency and parallelism?

  • Concurrency: Multiple tasks are executed in the same time period (not necessarily at the same time);
  • Parallel: Multiple tasks are executed simultaneously per unit of time.

What is a context switch?

In multithreaded programming, the number of threads is generally greater than the number of CPU cores, and a CPU core can only be used by one thread at any time. In order to make these threads can be effectively executed, the CPU takes the strategy of allocating time slices and rotation for each thread. A context switch occurs when one thread runs out of time and is ready again for use by another thread.

To summarize, the current task saves its state before switching to another task at the end of the CPU time slice, so that the next time it switches back to the task, it can load the state of the task again. The process of a task from saving to reloading is a context switch.

Context switches are often computationally intensive. In other words, it takes a considerable amount of processor time, in the order of nanoseconds for each of the tens or hundreds of switches per second. So context switching means a lot of CPU time for the system, and in fact, may be the most time consuming operation in the operating system.

Linux has a number of advantages over other operating systems, including other UniX-like systems. One of them is that context and mode switches are much less time-consuming.

What is a thread deadlock? How do I avoid deadlocks?

15.1. Recognize thread deadlock

Multiple threads are blocked at the same time, one or all of them waiting for a resource to be released. Because the thread is blocked indefinitely, the program cannot terminate normally.

As shown in the figure below, thread A holds resource 2 and thread B holds resource 1, and they both want to apply for each other’s resource, so the two threads wait for each other and enter A deadlock state.

Here’s an example of a thread deadlock, modeled by the code above (from the Beauty of Concurrent Programming) :

public class DeadLockDemo { private static Object resource1 = new Object(); Private static Object resource2 = new Object(); private static Object resource2 = new Object(); Public static void main(String[] args) {new Thread(() -> {synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource2"); synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); }}}, "thread 1").start(); new Thread(() -> { synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource1"); synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); }}, "thread 2").start(); }}Copy the code

Output

Thread[Thread 1,5,main]get resource1 Thread[Thread 2,5,main]get resource2 Thread[Thread 1,5,main]waiting get resource2 Thread[Thread 2, 5, the main] waiting get resource1Copy the code

Thread A obtains the monitor lock of Resource1 by synchronized (resource1), and then by thread.sleep (1000); Let thread A sleep for 1s so that thread B can execute and get the monitor lock of Resource2. When both threads A and B end their sleep and attempt to request each other’s resources, the two threads are in A state of waiting for each other, resulting in A deadlock. The above example meets the four conditions necessary to generate a deadlock.

Anyone who has studied operating systems knows that deadlocks must meet the following four conditions:

  1. Mutually exclusive: The resource can be occupied by only one thread at any time.
  2. Request and hold condition: when a process is blocked by a request for a resource, it holds on to a resource it has acquired.
  3. No deprivation condition: the obtained resources cannot be forcibly deprived by other threads before they are used up, and the resources can be released only after they are used up.
  4. Cyclic wait condition: Several processes form an end-to-end cyclic wait resource relationship.

15.2 How Can I Avoid Thread Deadlock?

We only need to break one of the four conditions that produce the deadlock.

Destroy the mutual exclusion condition

There is no way to break this condition, because the lock is intended to make them mutually exclusive (critical resources require mutually exclusive access).

Break request and hold conditions

Apply for all resources at once.

Destroy the condition of non-deprivation

If a thread that occupies part of a resource fails to apply for other resources, it can proactively release the resources it occupies.

Breaks cyclic wait conditions

Prevent this by requesting resources sequentially. If resources are applied for in a certain order, they are released in reverse order. Breaks cyclic wait conditions.

We changed the code for thread 2 to the following so that the deadlock does not occur.

new Thread(() -> { synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource2"); synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); }}, "thread 2").start();Copy the code

Output

Thread[Thread 1,5,main]get resource1 Thread[Thread 1,5,main]waiting get resource2 Thread[Thread 1,5,main]get resource2 Thread[Thread 1,5,main]get resource2 Thread[Thread 2,5,main]get resource1 Thread[Thread 2,5,main]waiting get resource2 Thread[Thread 2,5,main]get resource2 Process finished with exit code 0Copy the code

Let’s examine why the above code avoids deadlock.

Thread 1 first acquires the monitor lock of Resource1, at which point thread 2 cannot acquire it. Thread 1 then gets the monitor lock of Resource2. Thread 1 then releases the monitor lock on Resource1 and resource2, and thread 2 acquires it and executes. This breaks the break loop wait condition, thus avoiding deadlocks.

What are the differences between sleep() and wait()?

  • The main difference is that the sleep method does not release the lock, while the WAIT method does.
  • Both can pause the execution of a thread.
  • Wait is usually used for interthread interaction/communication, and sleep is usually used to suspend execution.
  • After wait() is called, the thread does not automatically wake up, requiring another thread to call notify() or notifyAll() on the same object. After the sleep() method completes, the thread wakes up automatically. Or you can use wait(Long Timeout) to automatically wake up the thread after a timeout.

Xvii. Why is the run() method executed when we call start(), and why can’t we call run() directly?

This is another classic Java multi-threaded interview question that is frequently asked in interviews. Very simple, but many people can not answer!

New a Thread, the Thread enters the new state; Calling the start() method starts a thread and puts it in the ready state, ready to run when the time slice is allocated. Start () performs the corresponding preparation of the thread, and then automatically executes the contents of the run() method, which is really multithreaded work. Directly executing the run() method, on the other hand, will execute the run method as if it were a normal method in the main thread, and will not execute it in a thread, so it is not multi-threaded.

Conclusion: The start method is called to start the thread and put the thread into the ready state, whereas the run method is just a normal method call of thread and is executed in the main thread.

reference

  • Blog.csdn.net/zhzhao999/a…
  • www.cnblogs.com/skywang1234…
  • www.cnblogs.com/Eason-S/p/5…

Open Source Project Recommendation

Other open source projects recommended by the author:

  1. JavaGuide: A Guide to Java Learning and Interviewing. It covers the core knowledge most Java programmers need to know.
  2. Springboot-guide: suitable for beginners and experienced developers to access the Spring Boot tutorial (part-time maintenance, welcome to maintenance).
  3. Advancement: I think technicians should have some good habits!
  4. Spring-security-jwt-guide: Getting Started from Zero! Spring Security With JWT (including permission authentication) back-end part of the code.

The public,