Three characteristics and seven principles of object orientation

Three characteristics and seven principles of object orientation

The three major characteristics

encapsulation

Encapsulation is a carrier class of properties and methods that can only be accessed through the interfaces (methods) they provide, hiding implementation details. The implementation is transparent to the programmer, and encapsulation benefits from changes made inside the class without affecting the rest of the code

  • Encapsulation practices: private properties (private modifiers modify properties), provide public read (getXX) write (setXX) methods, and call methods in constructs. All nonscalar attributes basically need to be encapsulated.
  • Benefits of encapsulation: Hides implementation details of classes, provides a uniform interface to all users, enhances execution, and is easy to maintain and extend

inheritance

Inheritance is a relationship that logically satisfies the parent of a subclass is a. Subclasses inherit the attributes and non-private methods of their parent class. Cannot inherit from a parent class construction, which uses the extends keyword, single-class inheritance, and multi-interface inheritance.

  • When constructing a subclass object, the parent class’s constructor is called in turn. (Subclasses default to call the parent class’s no-argument constructor. You can use super(argument list) to call the parameter construction of the specified parent class) up to Object. Call the subclass’s own; When a subclass calls a constructor of its parent class, the constructor of the parent class can only call one and must be written in the first sentence of the subclass constructor.

polymorphism

Polymorphism refers to allowing objects of different classes to respond to the same message. Polymorphism includes parametric polymorphism and inclusion polymorphism. Polymorphic language has the advantages of flexibility, abstraction, behavior sharing and code sharing, which solves the problem of application function having the same name. There are four types of polymorphism:

Basic types of polymorphism: unboxing, boxing.

The Java language encapsulates each of the eight data types in a class, which encapsulates the properties and basic operations of each basic data type.

The operation that automatically converts the basic type to the corresponding packaging class is called automatic packing operation, and vice versa is called automatic unpacking operation. Automatic packing operation has an automatic packing pool (ranging from -128 to 127). As long as the number of automatic packing is in the range of automatic packing pool, go directly to the pool to find the data.

Method polymorphism: overloading, overwriting.
  • Overriding (inheritance is a precondition for overriding): A primary can now change the implementation of a method that its parent inherits if it doesn’t look like a primary. Rewriting requirements:

    1. The return value, method name, and argument are the same (subclass type allowed after 5.0);
    2. Subclass exceptions cannot exceed parent class exceptions;
    3. The subclass access level cannot be lower than the parent class access level.
  • Overloading: When two or more methods of the same name exist in the same class, but the parameters are different (the number of arguments is different, the type is different, and the order is different <(int,String) and (String,int) are different >). The return value type can be the same or different. The most common example of overloading is constructors.

Class or interface polymorphism: a reference to a parent class refers to an object of a subclass

The parent’s reference to the child’s object (Person p = new Student()) is polymorphic.

  • Only properties and methods defined by the parent side can be used
  • Classes defined in subclasses cannot be used directly
  • A subclass overrides the methods of its parent class, and calls vary depending on whether the method is static(calling the parent class) or not (calling the subclass).
  • If you want to use a method defined in a subclass, you can cast itinstance ofOperator to determine the type of object)

The sample program

class A {
    int a = 1;
    static int b = 20;
    int c = 3;
    double d = 2.0;
    
    void show(a) {
        System.out.println("Class A: a=" + a + "\td=" + d + "\tc=" + c + "\tb=" + b);
    }
    
    void common(a){
        System.out.println("Class A: method common()");
    }
    
    static void execute(a){
        System.out.println("Class A: method excute()"); }}class B extends A {
    
    float a = 3.0 f;
    static int b = 30;
    int c = 5;
    String d = "Java program.";

    void show(a) {
        super.show();
        System.out.println("Class B: a=" + a + "\td=" + d + "\tc=" + c + "\tb=" +b);
    }
    
    void common(a){
        System.out.println("Class B: method common()");
    }
    
    static void execute(a){
        System.out.println("Class B: method execute()");
    }
    
    public static void main(String[] args) {
        A a = new B();
        a.show();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        a.common();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); a.execute(); }}Copy the code

Perform output

Class A: A = 1 c = 3 d = 2.0 20 Class b: b = A = 3.0 d = Java program. C = 5 b = 30 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the Class b: method common() ---------------------- Class A: method excute()Copy the code
Polymorphism in parameter passing

Is parameter passing in Java pass-by or pass-by?

Method calls in the Java language only support value passing of parameters, and when an object instance is passed to a method as a parameter, the value of the parameter is a reference to that object. Properties of objects can be changed during invocation, but changes to object references do not affect the caller.

The sample program

public class Test {

    public static void invoke(int num, Person person){
        num = 222;
        person.setAge(20);
        person.setName("Bill");
        System.out.println(num + "," + person.getName() + "," + person.getAge());
    }

    public static void main(String[] args) {
        int num = 111;
        Person person = new Person("Zhang".10);
        invoke(num, person);
        System.out.println(num + "," + person.getName() + "," + person.getAge());
    }

    @Data
    static class Person{

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        private String name;

        private intage; }}Copy the code

Program output

222Li si,20
111Li si,20
Copy the code

The seven principles

  1. The single responsibility principle (a class only does what it’s supposed to do),
  2. Open and closed principle (more modified closed, open expansion),
  3. Dependency inversion principle (interface oriented programming),
  4. Richter’s substitution principle (you can replace a parent type with a child type at any time),
  5. Interface isolation principle (interfaces should be small and specialized, never large and complete),
  6. The composite reuse principle (preferred reuse of code using aggregate or composite relationships),
  7. Demeter principle (Objects should be related to each other in as few ways as possible)

Java program initialization order

Java program initialization order

In The Java language, when an object is instantiated, all the member variables of the object’s class are instantiated first. Only after all the members of the class are instantiated, the constructor of the object’s class is called to create the object. Java program initialization generally follows three principles (in descending order of priority):

  • 1). Static objects (variables) are initialized prior to non-static objects (variables).
  • 2). Classes are initialized prior to subclasses.
  • 3). Initialize the member variables in the order they are defined. Even if variable definitions are interspersed with method definitions, they are initialized before any methods (including constructors) are called.

The sample program

class B {

    static {
        System.out.println("B with static ");
    }

    {
        System.out.println("B with code block");
    }

    public B(a){
        System.out.println("B with construct"); }}class A extends B {

    static {
        System.out.println("A with static");
    }

    {
        System.out.println("A with code block");
    }

    public A(a){
        System.out.println("A with construct"); }}Copy the code

Scenario 1 use case:

public class Test {

    public static void main(String[] args) {
        new B();
        System.out.println("-- -- -- -- -- -");
        newB(); }}Copy the code

Scenario 1 output:

B with static 
B with code block
B with construct
------
B with code block
B with construct
Copy the code

Scenario 2 use case:

public class Test {

    public static void main(String[] args) {
        newA(); }}Copy the code

Scenario 2 Output:

B with static 
A with static
B with code block
B with construct
A with code block
A with construct
Copy the code

Scenario 3 Use case:

public class Test {

    public static void main(String[] args) {
        new A();
        System.out.println("-- -- -- -- -- -");
        newA(); }}Copy the code

Scenario 3 Output:

B with static 
A with static
B with code block
B with construct
A with code block
A with construct
------
B with code block
B with construct
A with code block
A with construct    
Copy the code

Java language variable type

Java language variable type

In the Java language, there are only three types of variables: member variables, static variables, and local variables.

Member variables

The scope of a class member variable is the same as that of the instantiated object of the class. When the class is instantiated, the member variable is allocated in memory and initialized until the end of the life of the instantiated object.

Class member variables have four scopes, and the access permissions are in descending order: public > protected > Default > private.

  • Public: Indicates that the member variable or method is visible to all classes or objects and can be directly accessed by all classes or objects.
  • Protected: Indicates that the member variable or method is visible to itself and its subclasses and has no access to other classes or objects.
  • Default: Indicates that the member variable or method is visible only to itself and its classes in the same package. If the parent class and its subclasses are in different packages, they have no access permission.
  • Private: Indicates that the member variable or method is private and only the current class has access to it. Other classes or objects have no access to it.

Note that these modifiers can only modify member variables, not local variables. Private and protected cannot modify classes (only public, abstract, or final can be used to modify classes).

A static variable

Member variables that are static are called static or global variables. Unlike member variables, static variables are not dependent on a specific instance, but are shared by all instances. That is: whenever a class is loaded, the JVM allocates storage space to the static variables of the class. Thus, static variables can be accessed by class and variable names.

A local variable

The scope and visibility of a local variable is within its curly braces.

Understand Java- bit arithmetic

Bitwise operators are binary, including and, not, or, xor.

Basic unit of data

Before we look at the operators, let’s look at the basic units of data

  • BIT: A binary BIT(the smallest unit of data), such as 0
  • BYTE(BYTE): 1BYTE = 8 bits, for example, 01010110
  • KB(kilobytes): 1KB = 1024 bytes
  • MB: 1MB = 1024KB
  • GB: 1GB = 1024MB
  • TB: 1TB=1024GB

Basic data types in Java

Data length may vary from language to language, but here’s a look at the basic data length in the Java language

Data type and corresponding length

type The length of the
boolean
char 16bit
byte 8bit
short 16bit
int 32bit
long 64bit
float 32bit
double 64bit

The sample program

@Test
public void test(a){
   // Output: 1
   System.out.println(Byte.BYTES);
   // Output: 2
   System.out.println(Short.BYTES);
   // Output: 4
   System.out.println(Integer.BYTES);
   // Output: 8
   System.out.println(Long.BYTES);
   // Output: 4
   System.out.println(Float.BYTES);
   // Output: 8
   System.out.println(Double.BYTES);
   / / output: 11111111111111111111111111111110
   System.out.println(Integer.toBinaryString(-2));
   / / output: 1111111111111111111111111111111111111111111111111111111111111110
   System.out.println(Long.toBinaryString(-2L));
   char c = 'Sue';
   // Output: su
   System.out.println(c);
}
Copy the code

^ (exclusive or)

Two numbers involved in the operation, if the two corresponding bits are the same (binary), the result is 0, otherwise 1. 0^0=0, 1^0=1, 0^1=1, 1^1=0

Operation instructions

operation binary The decimal system
000000000000000000000000000000001 1
000000000000000000000000000000010 2
^ 000000000000000000000000000000011 3

Program testing

@Test
public void test(a){
   // Output: 3
   System.out.println(1^2);
}
Copy the code

& (and)

If both corresponding bits are 1(binary), the result is 1; otherwise, the result is 0. 0^0=0, 1^0=1, 0^1=1, 1^1=1

Operation instructions

operation binary The decimal system
000000000000000000000000000000001 1
000000000000000000000000000000010 2
& 000000000000000000000000000000000 0

Program testing

@Test
public void test(a){
   // Output: 0
   System.out.println(1&2);
}
Copy the code

| (or)

If only one of the corresponding bits is 1(binary), the result is 1, otherwise it is 0. 0^0=0, 1^0=1, 0^1=1, 1^1=1

Operation instructions

operation binary The decimal system
000000000000000000000000000000001 1
000000000000000000000000000000010 2
or 000000000000000000000000000000011 3

Program testing

@Test
public void test(a){
   // Output: 3
   System.out.println(1|2);
}
Copy the code

~ (not)

Parameter computes a number that results in 1 if the bit is 0(binary) and 0 if the bit is 1.

Operation instructions

binary The decimal system operation binary The decimal system
000000000000000000000000000000001 1 ~ 11111111111111111111111111111110 2 –
000000000000000000000000000000010 2 ~ 11111111111111111111111111111101 – 3

Program testing

@Test
public void test(a){

   // Output: -2
   System.out.println(~1);
   / / output: 11111111111111111111111111111110
   System.out.println(Integer.toBinaryString(~1));

   // Output: -3
   System.out.println(~2);
   / / output: 11111111111111111111111111111101
   System.out.println(Integer.toBinaryString(~2));
}
Copy the code

Interview questions about bitwise operations

【 Interview question 】 Implement the exchange of two numbers

If two numbers are xor, an intermediate number will be calculated (even if the two numbers are the same, then the result will be 0).

Code implementation

@Test
public void find(a){
   int a = 1, b=2;
   System.out.println("Before exchange: a =" + a + ", b = " + b);
   a = a ^ b;
   b = a ^ b;  // ((a^b) ^ b) = a
   a = a ^ b;  // ((a^b) ^ b) = a
   System.out.println("After exchange: a =" + a + ", b = " + b);
}
Copy the code

[Interview question] Implement string flipping

Idea: Use xor to convert high and low bits

Code implementation

/ * * *@paramSTR String to be reversed *@returnThe flipped string */
public static String reverse(String str){
   char[] chars = str.toCharArray();
   int low = 0;
   int top = chars.length - 1;
   while (low < top){
       chars[low] ^= chars[top];
       chars[top] ^= chars[low];
       chars[low] ^= chars[top];
       low++;
       top--;
   }
   return new String(chars);
}
Copy the code

【 Interview question 】 a number of numbers, except for one number only appear once, the other numbers appear 2n times (n>=1), find the number.

Analysis: Because the same two numbers do ^ result in 0, 0^ any number = any number. If you xOR the number from the first to the last, you end up with the number that occurs only once.

Code implementation

@Test
public void findOne(a){
   int[] arr = new int[] {1.1.2.2.3.5.5.7.7.6.6};
   // Start with the first index
   int result = arr[0];
   // Use this to perform xOR operations on the following data
   for(int i = 1; i < arr.length; i++){
       result = result ^ arr[i];
   }
   // Program output: 3
   System.out.println(result);
}
Copy the code

Java interview questions 】 【 | and | |, & and what is the difference between &&

  • &(and): bitwise operator that also functions as a logical budget character.

  • &&(short-circuited and): is only a logical operator. It has the following differences as a logical operator:

  • & : Whether the left side of & is false or not, he continues to test the Boolean values on the right.

  • &&: As long as the value on the left is detected to be false, it will directly judge the result, and will not check the value on the right (because “and” has a false, the final result is false), so the execution efficiency of && is higher, so it is generally used for logical operation.

  • | (or) and | | (short circuit or) difference with & (and) and && (short circuit is similar to).

The Object class in Java.

The Object class in Java

What methods are common to Object

  • 1. Clone method

Protection method, create and return a copy of this object is used to realize the object of shallow copy, only implements the Cloneable interface to invoke the method, otherwise throw CloneNotSupportedException exception; In JAVA, except for the 8 basic types of parameters that are passed by value, other class object parameters are passed by reference. Sometimes, we do not want to change the parameters in the method, so we need to clone the clone method in the class.

  • 2. GetClass method

Final method, which returns the runtime class of an object.

  • 3. ToString method

Returns a string representation of the object. This method is used more often, and generally subclasses have overrides.

  • 4. Finalize method

This method is used to release resources. This method is called by the object’s garbage collector when the garbage collector determines that no more references to the object exist. Subclasses override the Finalize method to configure system resources or perform other cleanup.

After the Finalize method of an object is enabled, no further actions will be performed until the Java Virtual machine determines once again that any thread that has not been terminated can no longer access the object through any method, including possible actions performed by other objects or classes that are to be terminated, at which time the object may be discarded.

The Java virtual machine calls the Finalize method at most once for any given object.

  • 5. Equals method

This method is a very important one. Equals and == are different in general, but in Object they are the same. Subclasses typically override this method.

  • 6. HashCode method

This method is used for hash lookups to reduce the number of times equals is used in lookups. Overriding equals usually overrides hashCode. This method is used in some collections that have hash capabilities.

Obj1.equals (obj2)==true1. Obj1.hashcode ()== obj2.hashcode () ‘can be derived, but hashCode equality does not necessarily satisfy equals. For efficiency, however, you should try to make the above two conditions nearly equivalent.

If you don’t override hashCode() and add two equals objects to the HashSet, both objects will be added.

  • 7. Wait

The wait method causes the current thread to wait for the lock on the object. The current thread must be the owner of the object, that is, the lock on the object. The wait() method waits until the lock is acquired or interrupted. Wait (long timeout) Sets a timeout interval and returns if the lock has not been acquired within the specified time.

This method is called and the current thread goes to sleep until the following event occurs.

    1. Other threads called notify on this object.
  • 2) Another thread calls notifyAll on this object.
  • 3) Another thread calls the interrupt to interrupt the thread.
  • 4) Time is up.

The thread can then be scheduled, throwing an InterruptedException if it is interrupted.

  • 8. Notify method

This method wakes up a thread waiting on the object.

  • 9. NotifyAll method

This method wakes up all threads waiting on the object.

The interview extension

Is Java passed by value or by reference

Java refers to passing, which is a value for a primitive type and the address of a pointer for a reference type

  • Value passing: When a method is called, the actual parameter passes its value to the corresponding formal parameter. Changes in formal parameter values during method execution do not affect the actual parameter values.

  • Reference: also known as the address, the method invocation of actual parameters reference (address, rather than the value of the parameter) is passed to the method of the corresponding formal parameter, to the operation of the formal parameter in the method performs is actually to the operation of the actual parameters, the method performs in the form of the change of the parameter values will influence the actual parameter values.

【 interview question 】 What is the difference between final, finally and Finalize

  • Final: modifiers (keywords) can be used in three ways: (1) to modify a class: to indicate that the class cannot be inherited; (2) modification method: indicates that the method cannot be overwritten; (3) Modify variable: indicates that the value of a variable can only be assigned once and cannot be modified (constant)

  • Finally: Usually placed in try… The catch… The following construct always executes a block of code (the return statement in try{}, followed by the finally{} code that returns the method to the caller). This means that the program will execute whether it executes normally or if an exception occurs. The code here can execute as long as the JVM is not shut down.

  • Finalize: A method defined in the Object class, which in Java allows you to use finalize() to do necessary cleanup (such as closing connections, closing files) before the garbage collector clears objects from memory. The call to Finalize (), which is not normally shown, is usually called by the garbage collector when objects are destroyed, by overriding the Finalize () method to clean up system resources or perform other cleanup tasks.

What is the difference between equals and ==

  • == compares the (heap) memory addresses of objects stored in the variable (stack) memory to determine whether the addresses of two objects are the same, that is, whether they are the same object. We are comparing pointer operations in the true sense.

  • Equals () compares whether the contents of two objects are equal. Since all classes inherit from java.lang.Object, equals() can return true or false depending on the implementation logic used to override equals.

Code sample

public static void main(String[] args) {
   String a = new String("ab"); // a is a reference
   String b = new String("ab"); // b is another reference, the object has the same content
   String aa = "ab"; // Put it in the constant pool
   String bb = "ab"; // Search from the constant pool
   if (aa == bb) // true
       System.out.println("aa==bb");
   if (a == b)   // false, different 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

Is the value of the object the same (x.equals(y) is true, but can have different hash values?

If two objects satisfy equals to true (x.equals(y)==true), then their hash codes must be the same.

If two objects have the same hashCode, they are not necessarily the same.

【 Interview questions 】 How to resolve Hash conflicts

By constructing hash functions with good performance, conflicts can be reduced, but generally it is impossible to avoid them completely, so resolving conflicts is another key problem of hash. Creating a hash table and looking up a hash table will run into conflicts, and you should resolve the conflicts the same way in both cases. The following uses creating a hash table as an example to explain how to resolve conflicts. There are four commonly used conflict resolution methods:

  • Open addressing

Also known as open hashing

The basic idea is: when the hash address of key p=H(key) conflicts, another hash address p1 is generated based on P. If P1 still conflicts, another hash address p2 is generated based on P. ; Until a non-conflicting hash address PI is found and the corresponding element is stored there.

This method has a general rehash function form: Hi = (H(key) + di) % m where I = 1,2… N.

Note: H(key) is the hash function, m is the table length, di is called the incremental sequence, the value of the incremental sequence is different, the corresponding rehash method is also different, mainly including the following three:

  • Linear detection and hashing

Di I = 1,2,3… , m-1 This method is characterized by: when a conflict occurs, the next cell in the table will be viewed sequentially until an empty cell is found or the whole table is searched.

  • Second probe and then hash

Di = 12, -12, 22, -22… , k2, -k2 (k<=m/2) This method is characterized by: when the conflict occurs, the left and right of the table for jump detection, relatively flexible.

  • Pseudo random detection and hashing

Di = sequence of pseudo-random numbers

example

For example, given that the length of the hash table is m=11 and the hash function is H(key) = key % 11, then H(47) =3, H(26) =4, and H(60) =5. Assuming that the next keyword is 69, then H(69) =3, which conflicts with 47.

  • Resolving conflicts with linear probing and hashing: H3= (3 + 3) % 11 = 6 H3= (3 + 3) % 11 = 6 H3= (3 + 3) % 11 = 6 H3= (3 + 3) % 11 = 6

  • The next hash address is H1=(3 + 12)% 11 = 4, still in conflict, then find the next hash address is H2= (3-12)% 11 = 2, now no conflict, fill 69 into unit 2.

  • Conflict handling with pseudorandom detection and hashing: set the sequence of pseudorandom numbers as: 2,5,9… . , then the next hash address is H1= (3 + 2) % 11 = 5, still in conflict, then find the next hash address is H2= (3 + 5) % 11 = 8, now there is no conflict, fill 69 into unit 8.

  • Then the hash method

Hash functions Hi=RH1(key) I =1, 2… , k

If the hash address Hi=RH1(key) conflicts, then calculate the hash address Hi=RH2(key)… Until there is no more conflict. This method does not produce aggregation easily, but increases the calculation time.

  • Chain address method

Basic idea: all records with the same hash address are linked in the same single linked list; The head pointer of the single linked list is stored in the ith cell of the hash table, so the search, insertion and deletion are mainly carried out in the synonym chain.

The chained address method is suitable for frequent inserts and deletions.

  • Establish public overflow areas

Basic idea: hash table is divided into two parts: basic table and overflow table, all elements that conflict with the basic table are filled into the overflow table

Java thread

Java thread

Thread state

A thread has basically five states: New, ready, running, blocked, and dead.

  • New State (New)

Thread t = new MyThread(); Thread t = new MyThread();

  • Runnable state

When the start() method of the thread object is called, the thread enters the ready state. When a thread is in the ready state, it is ready to be executed by the CPU, not when t.start() is executed. Such as: t.s tart ();

  • Running Status

When the CPU starts dispatching the thread in the ready state, the thread can actually execute, that is, enter the running state. Note: The ready state is the only entry into the running state, that is, in order to enter the running state execution, the thread must first be in the ready state.

  • The state of being Blocked

For some reason, a thread in the running state temporarily gives up the right to use the CPU and stops executing. At this time, it enters the blocking state. Until it enters the ready state, it has the opportunity to be called by the CPU again to enter the running state. According to different causes of blocking, blocking states can be divided into three types:

  1. Waiting for blocking

A running thread executes wait() to block the thread.

  1. A synchronized block

A thread that fails to acquire a synchronized lock (because the lock is occupied by another thread) enters a synchronized blocking state.

  1. Other block

A thread blocks by calling its sleep() or join() or by making an I/O request. When the sleep() state times out, when the join() wait thread terminates or times out, or when I/O processing is complete, the thread goes back to the ready state.

  • Dead state

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

Thread life cycle

A thread’s declaration cycle typically starts in a New state and ends in a Dead state, with many possibilities in between.

As shown in the figure above, there are generally four branching cases:

  • Normal (red arrow),

  • Lock blocking (synchronous blocking) occurs (blue arrow),

  • Wait blocking occurs (yellow arrow),

  • Other blocking situations (black arrow) correspond to the four different colored flows in the figure above

  • normal

As shown by the red arrow in the figure above, the normal thread declaration cycle is NEW -> RUNNABLE -> RUNNING -> DEAD.

  • Lock blocking occurs (synchronous blocking)

Lock blocking occurs when a thread enters a synchronized method block while in the RUNNING state, as shown by the blue arrow in the figure above, and the thread enters a Lock Pool. When it acquires the lock, it enters the runnable state again. When the CPU sharding polls it, it runs again until it is DEAD.

  • Wait blocking occurs

As shown by the blue arrow in the figure above, wait blocking occurs when a thread encounters a wait() method while in the RUNNING state and waits for another thread to call notify() to release the lock before returning to the runnable state. When the CPU sharding polls it, it runs again until it is DEAD. Wait blocking and lock blocking are actually the same type. They are threads waiting for locks. The only difference is that they are implemented in different ways, but the underlying principle is the same. Note that to execute a wait(), the thread must acquire the lock, so a wait() method is usually in a synchronized method or block of code. When it acquires the lock, it enters the wait pool and releases the lock. After receiving the notify() notice, wait for the lock to be acquired before running.

  • Other blocks occur (e.g., IO reads, etc.)

Blocking occurs when a thread needs to read a file that is occupied by another thread. In this case, a thread must wait for another thread to finish reading before it can continue. This is called IO blocking. Of course, there are many other conditions, such as network congestion and so on

CountDownLatch application

CountDownLatch is a synchronization helper class that allows a thread to wait for other threads to complete their work before executing. For example, the main thread of an application wants to execute after the thread responsible for starting the framework service has started all the framework services.

CountDownLatch implementation overview

CountDownLatch is implemented through a counter whose initial value is the number of threads. Each time a thread completes its task, the counter is reduced by one. When the counter value reaches 0, it indicates that all threads have completed the task, and the threads waiting on the lock can resume the task.

CountDownLatch multithreaded application

List loop traversal (8697ms)

@Test
public void optimizeListV1(a){
    long start = System.currentTimeMillis();
    try {
        final List<String> lists = Arrays.asList("aa"."bb"."cc"."dd"."ee");
        for(int i=0; i<lists.size(); i++){
            if(i == 2){
                Thread.sleep(3000);
            }
            Thread.sleep(1000);
        }
        System.out.println("Aggregation complete");
    }catch (Exception e){

    }finally {
        MockTimeUtil.mockInvokeTime("Loop list scenario simulation :", start); }}Copy the code

Multi-threaded aggregated list (4671ms)

@Test
public void optimizeList(a){
    long start = System.currentTimeMillis();
    try {
       ExecutorService ex = Executors.newFixedThreadPool(5);
       final List<String> lists = Arrays.asList("aa"."bb"."cc"."dd"."ee");
       final CountDownLatch latch =  new CountDownLatch(lists.size());
       for(int i=0; i<lists.size(); i++){
           final int tmp = i;
           ex.submit(new Callable<Object>() {
               @Override
               public Object call(a) throws Exception {
                   if(tmp == 2){
                       Thread.sleep(3000);
                   }
                   Thread.sleep(1000);
                   latch.countDown();
                   return null; }}); }//latch.await();
       latch.await(3500, TimeUnit.MILLISECONDS);
       System.out.println("Aggregation complete");
   }catch (Exception e){

   }finally {
       MockTimeUtil.mockInvokeTime("Thread list scenario simulation :", start); }}Copy the code

CountDownLatch method description

CountDownLatch source

public class CountDownLatch {
    /** * Synchronization control For CountDownLatch. * Uses AQS state to represent count. */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount(a) {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0)?1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0; }}}private final Sync sync;

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    public void await(a) throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    public void countDown(a) {
        sync.releaseShared(1);
    }

    public long getCount(a) {
        return sync.getCount();
    }

    public String toString(a) {
        return super.toString() + "[Count = " + sync.getCount() + "]"; }}Copy the code

Method description:

  • CountDown () : This method is called by the current thread and the count is reduced by one
  • Await (): Calling this method blocks the current thread until the value of the timer is 0

CompleteService application

CompleteService use scenario

When we need to batch tasks, but do not care about the order in which they are completed, we asynchronously submit tasks, wait for the completion of a task and then process the completion result, and so on until the batch task is completed. When we follow the principle of operation after asynchronous processing, the first one to complete the harvest.

Traversal processing based on the collection Future

For this scenario, we would probably want to collect all the asynchronous tasks into a collection, iterate over the collection (Future), call future.get() to get the results of the processing, and then write the following code

ExecutorService pool = Executors.newFixedThreadPool(5);
final List<String> dList = Arrays.asList("aa"."bb"."cc"."dd"."ee");
List<Future> fList= new ArrayList<Future>();
for(int i=0; i<dList.size(); i++){
    final int tmp = i;
    Future future =  pool.submit(new Callable<Object>() {
        @Override
        public Object call(a) throws Exception {
            if (tmp == 2) {
                Thread.sleep(3000);
            }
            Thread.sleep(1000);
            return "Thread" + Thread.currentThread().getName() + "Process the data element list(" +  + tmp +") ="+ dList.get(tmp) ; }}); fList.add(future); } System.out.println("Aggregation complete");
for (int i = 0; i < fList.size(); i++) {
    System.out.println(fList.get(i).get());
}
Copy the code

When we execute this code, we find that the execution result is not what we expected

List (1) = bb pool-1-thread-3 = cc List (3) = list(4) = eeCopy the code

The result of the above execution is not what we want. It is obvious that the cc element is time-consuming to execute, but the result of our processing is in the order of the loop. The reasons are as follows:

Each Future object traversed through the list is not necessarily in a completed state, and calling the get() method blocks. If the system is designed so that each thread completes and continues to do something based on its results, this adds extra waiting time for threads that finish first but are behind the list.

Complete parallel aggregation based on CompletionService

ExecutorService pool = Executors.newFixedThreadPool(5);
CompletionService<Object> cs = new ExecutorCompletionService<Object>(pool);
final List<String> dList = Arrays.asList("aa"."bb"."cc"."dd"."ee");
for(int i=0; i<dList.size(); i++){
    final int tmp = i;
    cs.submit(new Callable<Object>() {
        @Override
        public Object call(a) throws Exception {
            if (tmp == 2) {
                Thread.sleep(3000);
            }
            Thread.sleep(1000);
            return "Thread" + Thread.currentThread().getName() + "Process the data element list(" +  + tmp +") ="+ dList.get(tmp); }}); } System.out.println("Aggregation complete");
for (int i = 0; i < dList.size(); i++) {
    System.out.println(cs.take().get());
}
Copy the code

The implementation will find that this result is what we really want

List (1) = BB thread POOL-1-thread-1 process data elements list(0) = AA thread pool-1-thread-4 process data elements list(3) = dd List (2) = ccCopy the code

We get the result we want because CompleteService maintains a BlockingQueue internally. The principle is as follows:

The implementation of CompletionService maintains a BlockingQueue that holds the Future object. The Future will only be added to the Queue if its state is finished. The take() method is the Consumer of producer-consumer. It will pull the Future from the Queue, and if the Queue is empty, it will block there until a finished Future is added to the Queue.

CompleteService the difference between take() and poll()

See the interface definition for CompleteService

public interface CompletionService<V> {

    Future<V> submit(Callable<V> task);

    Future<V> submit(Runnable task, V result);

    Future<V> take(a) throws InterruptedException;

    Future<V> poll(a);

    Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
Copy the code

From the interface we can see that the Summit-related methods defined in CompletionService are used to load the thread body (which handles threads implementing Callable or Runable, respectively), and poll() and take() are used to retrieve the returned result set.

On the difference between poll() and take()

Poll () is non-blocking, returns a null if there is no result, and the thread continues to run without blocking. Take () blocks, and if there is no result, the thread blocks until a result is produced

Example difference between poll() and take()
ExecutorService pool = Executors.newFixedThreadPool(5);
CompletionService<Object> cs = new ExecutorCompletionService<Object>(pool);
final List<String> dList = Arrays.asList("aa"."bb"."cc"."dd"."ee");
for(int i=0; i<dList.size(); i++){
    final int tmp = i;
    cs.submit(new Callable<Object>() {
        @Override
        public Object call(a) throws Exception {
            if (tmp == 2) {
                Thread.sleep(3000);
            }
            Thread.sleep(1000);
            return "Thread" + Thread.currentThread().getName() + "Process the data element list(" +  + tmp +") ="+ dList.get(tmp); }}); } System.out.println("Aggregation complete");
AtomicInteger index = new AtomicInteger(0);
while(index.get()<dList.size()) {
    Future<Object> f = cs.poll();
    if(f == null) {
        System.out.println("No mission was completed.");
    }else {
        System.out.println(f.get());
        index.incrementAndGet();
    }
    Thread.sleep(500);
}
Copy the code

Program running result

Thread pool-1-thread-1 Processes data elements list(0) = aa Thread pool-1-thread-4 processes data elements list(3) = dd Thread pool-1-thread-2 processes data elements list(1) = thread POOL-1-thread-5 processes data elements list(4) = EE No completed task is found Thread pool-1-thread-3 processes data elements list(2) = ccCopy the code

JavaIO model

JavaIO model

In high-performance IO architecture design, there are a few terms that need to be outlined.

Synchronous and asynchronous, blocking and non-blocking

Synchronous and asynchronous is aimed at the application from the perspective of the interaction with the kernel, blocking and non-blocking is aimed at the process at the time of access to data, according to the ready state of IO operations to take different ways, in plain English is a read or write operation function is implemented, blocking the way to read and write functions will have been waiting for, rather than blocking mode, The read or write function returns a status value immediately. In short: Synchronous and asynchronous are the destination, blocking and non-blocking are the implementation.

  • Synchronization: when a user process triggers an I/O operation and waits or polls to see if the OPERATION is ready.
  • Asynchronous: after the user process triggers the IO operation, it will start to do its own work. When the IO operation is completed, it will get the notification of the completion of THE IO (the characteristic of asynchrony is notification). It will tell friends the size, size and color of the appropriate clothes, and let friends entrust to sell them, and then it can do other things.
  • Blocking: Blocking means that when attempting to read or write to the file descriptor, if there is nothing to read or write to at that time, the program enters a waiting state until there is something to read or write to. For example, we go to the bus station to recharge our account and find that at this time, the account teller is not there (maybe he has gone to the toilet), and then we wait here until the account teller comes back.
  • Non-blocking: In a non-blocking state, if there is nothing to read or write, the read-write function returns immediately without waiting. For example: the bank withdrawals do business, get a small ticket, after getting our own can play mobile phone, or chat with others, when we turn, the speaker of the bank will notice, at this time we can go.

IO model in Java

  • There are three main IO models in Java: blocking IO (BIO), non-blocking IO (NIO), and asynchronous IO (AIO).

  • IO apis provided in Java depend on IO operations at the operating system level for file processing. For example, after Linux 2.6, BOTH NIO and AIO are implemented via epoll in Java, while AIO is implemented via IOCP on Windows.

Synchronous blocking IO(JAVA BIO)

Synchronization and blocking, the server implementation mode is one connection one thread, that is, when the client has a connection request, the server needs to start a thread for processing, if the connection does not do anything will cause unnecessary thread overhead, of course, can be improved through the thread pool mechanism.

Synchronous non-blocking IO(Java NIO)

Synchronous non-blocking, the server implementation mode is one request one thread, that is, the connection request sent by the client is registered with the multiplexer, the multiplexer polls the connection to have I/O requests only start a thread for processing. The user process also needs to periodically ask if the IO operation is ready, which requires the user process to keep asking.

Asynchronous blocking IO(Java NIO)

In this way, the application does not wait for the kernel to complete the IO operation, and the kernel will notify the application when the IO operation is completed. This is the key difference between synchronous and asynchronous. Synchronization must wait or actively ask whether the IO has completed. Because this is done through the SELECT system call, which itself blocks, the advantage of using the select function is that it can listen for multiple file handles at the same time. (From an UNP perspective, select is a synchronous operation. Because after select, the process also needs to read and write data) to improve the system concurrency!

Asynchronous non-blocking IO(Java AIO(NIO.2))

In this mode, the user process only needs to initiate a IO operations and returns immediately, such as IO operations after the completion of the true, the application will receive a phone call from the IO operation is completed, then the user process only needs to deal with the data, do not need to undertake the actual I/o read and write operations, because the real IO read or write operation has been completed by the kernel.

BIO, NIO, and AIO application scenarios

  • BIO mode is suitable for small and fixed number of connections. This mode requires high server resources, concurrency is limited to the application, and the only choice before JDK1.4, but the program is intuitive, simple and easy to understand.
  • NIO is suitable for architectures with a large number of connections and relatively short (light operation) connections, such as chat servers, where concurrency is limited to applications and programming is complicated. JDK1.4 supports NIO.
  • AIO mode is used in the architecture with a large number of connections and long connections (heavy operation), such as photo album server. It fully calls OS to participate in concurrent operations, and the programming is complicated, which is supported by JDK7.

IO model in Linux

  • There are five IO models in Linux(UNIX) : blocking IO model, non-blocking IO model, signal-driven IO model, IO multiplexing model and asynchronous IO model.

Blocking IO model

Blocking I/O, the simplest I/O model, typically involves a process or thread waiting for a condition, and then waiting forever if the condition is not met. If the conditions are met, go to the next step.

The application process receives the data through the system call recvFROM, but the application process blocks because the kernel is not ready for the datagram. The application process cannot stop blocking until the kernel is ready for the datagram and RecvFROM finishes copying the datagram.

Non-blocking IO model

Non-blocking IO model. The application process interacts with the kernel, and instead of just waiting, it simply returns. Then, through polling, constantly ask the kernel if the data is ready. If a poll finds that the data is ready, the data is copied to user space.

The application process keeps interacting with the kernel through recvFROM calls until the kernel is ready for data. If not, the kernel returns an error, and the application process sends the recvFROM request some time after receiving an error. The process can do something else first between requests.

Signal driven IO model

The application process tells the kernel when it reads a file, and if an event occurs on a socket, please send me a signal. After receiving the signal, the corresponding signal processing function will perform subsequent processing.

The application process registers a signal handler with the kernel in advance, and the user process returns without blocking. When the kernel data is ready, a signal is sent to the process, and the user process begins copying data from the signal handler into user space.

IO multiplexing model

The IO of multiple processes can be registered with the same pipe, which will interact with the kernel uniformly. When the data required for a request in the pipeline is ready, the process copies the corresponding data into user space.

When a user process invokes the SELECT function, the SELECT process will listen to all the registered I/OS. If the data required by all the monitored I/OS is not ready, the select calling process will block. When the data for any IO is ready, the select call returns, and the process copies the data through recvFROM.

The IO multiplexing model does not register signal handlers with the kernel, so it is not non-blocking. The process does not return a select until at least one of the IO operations monitored by the select is ready, and it also needs to send another request to copy the file.

Asynchronous IO model

Asynchronous IO model. After the application process passes the IO request to the kernel, the kernel handles the file copy completely. After the kernel completes related operations, it sends a signal to inform the application process that the I/O is complete.

After the user process initiates the AIo_read operation, it passes the descriptor, buffer pointer, buffer size, and so on to the kernel, telling the kernel how to notify the process when the entire operation is complete, and then immediately does something else. When the kernel receives aiO_read, it returns immediately and waits for the data to be ready. When the data is ready, the kernel copies the data directly to the user control and then informs the process that the I/O is complete.

Comparison of 5 IO models

Description:

  • Blocking IO model, non-blocking IO model, IO multiplexing model and signal-driven IO model are all synchronous IO models. The reason is that, regardless of the above model, the real data copy process is synchronous.
  • While signals drive the IO model, the kernel notifies the process when the data is ready, and the process copies the data through the recvFROM operation. We can think of the data preparation phase as asynchronous, but the data copy operation is synchronous. Therefore, the whole IO process cannot be considered asynchronous.

reference

  • www.cnblogs.com/zjc950516/p…
  • Blog.csdn.net/qq_33098049…
  • www.cnblogs.com/wuchaodzxx/…
  • Blog.csdn.net/yeiweilan/a…
  • www.cnblogs.com/qqzy168/p/3…
  • Jingyan.baidu.com/article/4f3…
  • www.cnblogs.com/moongeek/p/…
  • www.jianshu.com/p/25e243850…
  • Mp.weixin.qq.com/s/fVpb1R0o2…
  • www.cnblogs.com/xingzc/p/57…
  • cmsblogs.com/?cat=183
  • www.cnblogs.com/lwbqqyumidi…
  • www.cnblogs.com/mengdd/arch…