Advanced java -- thread pool, Lambda expression

Advanced java (VII) -- thread pool and Lambda expression

Portal: ·Zhang Qi MAX·


##Learning objectives

-[] able to understand the concept of thread communication
-[] be able to understand the waiting wake-up mechanism
-[] be able to describe the operation principle of Java thread pool
-[] be able to understand the advantages of functional programming over object-oriented programming
-[] be able to master the standard format of Lambda expression
-[] be able to use Lambda standard format and use Runnable and Comparator interface
-[] be able to master the ellipsis format and rules of Lambda expression
-[] be able to use Lambda ellipsis format, and use Runnable and Comparator interface
-[] be able to use customized interfaces (with only one abstract method) through Lambda's standard format
-[] the user-defined interface (with only one abstract method) can be used through Lambda's ellipsis format
-[] be able to clarify the two preconditions of Lambda

Chapter I waiting wake-up mechanism

1.1 inter thread communication

**Concept: * * multiple threads are processing the same resource, but the processing actions (tasks of threads) are different.

For example, thread A is used to generate steamed stuffed buns and thread B is used to eat steamed stuffed buns. Steamed stuffed buns can be understood as the same resource. The actions processed by thread A and thread B are production and consumption, so there is A thread communication problem between thread A and thread B.

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-jh77bzm4-16209583517) (IMG \ inter thread communication. bmp)]

Why handle inter thread communication:

When multiple threads execute concurrently, the CPU switches threads randomly by default. When we need multiple threads to complete a task together, and we want them to execute regularly, some coordinated communication is needed between multiple threads to help us operate a piece of data together.

How to ensure inter thread communication and make effective use of resources:

When multiple threads are dealing with the same resource and different tasks, thread communication is needed to help solve the use or operation of the same variable between threads. That is, multiple threads avoid competing for the same shared variable when operating the same data. That is, we need to use certain means to make each thread make effective use of resources. This means is the waiting wake-up mechanism.

1.2 waiting for wake-up mechanism

What is the waiting wake-up mechanism

This is a collaboration mechanism between multiple threads. When it comes to threads, we often think of the race between threads, such as competing for locks, but this is not the whole story. There will also be a cooperation mechanism between threads. Just like you and your colleagues in the company, you may have competition for promotion, but more often you work together to complete certain tasks.

That is, after a thread has performed the specified operation, it enters the waiting state (wait()) and wakes up after other threads have executed their specified code (notify()); When there are multiple threads waiting, you can use notifyAll() to wake up all waiting threads if necessary.

wait/notify is a cooperative mechanism between threads.

Method of waiting for wake-up

The wake-up waiting mechanism is used to solve the problem of inter thread communication. The meanings of the three methods used are as follows:

  1. Wait: the thread is no longer active, no longer participates in scheduling, and enters the wait set. Therefore, CPU resources will not be wasted and lock competition will not occur. At this time, the thread state is WAITING. It also waits for other threads to perform a special action, that is, to "notify" the threads WAITING on this object to be released from the wait set and re-enter the ready queue
  2. notify: select a thread in the wait set of the notified object to release; For example, when a restaurant has a free seat, the customer waiting for the longest meal takes the first seat.
  3. notifyAll: release all threads on the wait set of the notified object.

be careful:

Even if only one waiting thread is notified, the notified thread cannot resume execution immediately, because the place where it was interrupted was in the synchronization block, and now it does not hold the lock, so she needs to try to obtain the lock again (it is likely to face competition from other threads). After success, she can resume execution at the place after calling the wait method.

The summary is as follows:

  • If the lock can be obtained, the thread will change from WAITING state to RUNNABLE state;
  • Otherwise, when it comes out of the wait set and enters the entry set, the thread will change from the WAITING state to the BLOCKED state

Details of calling wait and notify methods

  1. The wait method and notify method must be called by the same lock object. Because: the corresponding lock object can wake up the thread after using the wait method called by the same lock object through notify.
  2. The wait method and notify method belong to the methods of Object class. Because: the lock Object can be any Object, and the class of any Object inherits the Object class.
  3. The wait method and notify method must be used in the synchronization code block or synchronization function. Because: these two methods must be called through the lock object.

1.3 producer and consumer issues

The waiting wake-up mechanism is actually a classic problem of "producers and consumers".

Take the production of steamed stuffed buns and the consumption of steamed stuffed buns as an example. How can the waiting wake-up mechanism make effective use of resources:

Steamed stuffed bun shop threads produce steamed stuffed buns, and eating threads consume steamed stuffed buns. When there is no steamed stuffed bun (the status of steamed stuffed bun is false),The eating thread waits, and the baozi shop thread produces baozi (that is, the baozi status is true),And notify the eating thread (release the waiting state of eating),Because there are already steamed stuffed buns, the steamed stuffed buns shop thread enters the waiting state. Next, whether the eating thread can further execute depends on the acquisition of the lock. If the lock is obtained from the food, the action of eating steamed stuffed buns is executed, and the steamed stuffed buns are finished (the status of steamed stuffed buns is false),And notify the thread of the baozi shop (release the waiting state of the baozi shop),The eating thread is waiting. Whether the packet shop thread can further execute depends on the acquisition of the lock.

Code demonstration:

Package resource class:

public class BaoZi {
     String  pier ;
     String  xianer ;
     boolean  flag = false ;//Whether the package sub resource has package sub resource status
}

Eating thread class:

public class ChiHuo extends Thread{
    private BaoZi bz;

    public ChiHuo(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }
    @Override
    public void run() {
        while(true){
            synchronized (bz){
                if(bz.flag == false){//No steamed stuffed bun
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("The food is eating"+bz.pier+bz.xianer+"steamed stuffed bun");
                bz.flag = false;
                bz.notify();
            }
        }
    }
}

Package shop thread class:

public class BaoZiPu extends Thread {

    private BaoZi bz;

    public BaoZiPu(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }

    @Override
    public void run() {
        int count = 0;
        //Steamed stuffed bun
        while(true){
            //synchronization
            synchronized (bz){
                if(bz.flag == true){//Package resource exists
                    try {

                        bz.wait();

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // There are no steamed buns to make steamed buns
                System.out.println("The steamed stuffed bun shop began to make steamed stuffed buns");
                if(count%2 == 0){
                    // Ice skin five kernel
                    bz.pier = "Ice skin";
                    bz.xianer = "Wuren";
                }else{
                    // Thin skinned beef and scallion
                    bz.pier = "Thin skin";
                    bz.xianer = "Beef and scallion";
                }
                count++;

                bz.flag=true;
                System.out.println("The steamed stuffed bun is made:"+bz.pier+bz.xianer);
                System.out.println("Let's eat");
                //Wake up waiting thread (eating)
                bz.notify();
            }
        }
    }
}

Test class:

public class Demo {
    public static void main(String[] args) {
        //Waiting for wake-up cases
        BaoZi bz = new BaoZi();

        ChiHuo ch = new ChiHuo("foodie",bz);
        BaoZiPu bzp = new BaoZiPu("Steamed stuffed bun shop",bz);

        ch.start();
        bzp.start();
    }
}

Execution effect:

The steamed stuffed bun shop began to make steamed stuffed buns
 Steamed stuffed bun: ice skin and five kernels
 Let's eat
 The food is eating five kernel steamed stuffed buns with ice skin
 The steamed stuffed bun shop began to make steamed stuffed buns
 Steamed stuffed bun: thin skinned beef and scallion
 Let's eat
 The food is eating thin skinned beef, scallion and steamed stuffed bun
 The steamed stuffed bun shop began to make steamed stuffed buns
 Steamed stuffed bun: ice skin and five kernels
 Let's eat
 The food is eating five kernel steamed stuffed buns with ice skin

Chapter 2 thread pool

2.1 overview of thread pool idea

[the transfer of external chain pictures fails. The source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-yxay6h61-16209583522) (IMG \ swimming pool. jpg)]

When we use threads, we create a thread, which is very easy to implement, but there will be a problem:

If there are a large number of concurrent threads and each thread executes a task for a short time, the efficiency of the system will be greatly reduced because it takes time to create and destroy threads frequently.

So is there a way to make threads reusable, that is, after executing a task, they can continue to execute other tasks without being destroyed?

In Java, this effect can be achieved through thread pool. Today, let's talk about java thread pool in detail.

2.2 thread pool concept

  • *** * threads can be created repeatedly without consuming too many threads to create a container.

Since many operations in the thread pool are related to optimizing resources, we won't repeat them here. Let's understand the working principle of thread pool through a diagram:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-ciakovui-16209583525) (IMG \ thread pool principle. bmp)]

Rational use of thread pool can bring three benefits:

  1. Reduce resource consumption. The number of threads created and destroyed is reduced. Each working thread can be reused and can perform multiple tasks.
  2. Improve response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.
  3. Improve thread manageability. You can adjust the number of work line threads in the thread pool according to the system's affordability to prevent the server from getting tired due to excessive memory consumption (each thread needs about 1MB of memory. The more threads are opened, the more memory will be consumed, and finally crash).

2.3 use of thread pool

The top-level interface of thread pool in Java is Java util. concurrent. Executor, but strictly speaking, executor is not a thread pool, but a tool for executing threads. The real thread pool interface is Java util. concurrent. ExecutorService.

Configuring a thread pool is complex, especially when the principle of thread pool is not very clear, it is likely that the configured thread pool is not better, so in Java util. concurrent. Executors thread factory class provides some static factories to generate some common thread pools. Officials recommend using the executors project class to create thread pool objects.

There is a method to create thread pool in Executors class as follows:

  • public static ExecutorService newFixedThreadPool(int nThreads): returns the thread pool object. (a bounded process pool is created, that is, the maximum number of threads in the pool can be specified)

After obtaining a thread pool ExecutorService object, how to use it? Here is a method to use the thread pool object as follows:

  • public Future<?> Submit (runnable task): get a thread object in the thread pool and execute it

    Future interface: used to record the results generated after the execution of thread tasks. Thread pool creation and use.

To use the thread object in the thread pool:

  1. Create a thread pool object.
  2. Create a Runnable interface subclass object. (task)
  3. Submit Runnable interface subclass object. (take task)
  4. Close the thread pool (not normally).

Runnable implementation class code:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("I want a coach");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Here comes the coach: " + Thread.currentThread().getName());
        System.out.println("Teach me to swim,After handing it in, the coach returned to the swimming pool");
    }
}

Thread pool test class:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // Create thread pool object
        ExecutorService service = Executors.newFixedThreadPool(2);//Contains 2 Thread objects
        // Create Runnable instance object
        MyRunnable r = new MyRunnable();

        //How to create thread objects yourself
        // Thread t = new Thread(r);
        // t.start(); --->  Call run() in MyRunnable

        // Gets the thread object from the thread pool and then calls run() in MyRunnable.
        service.submit(r);
        // Get another thread object and call run() in MyRunnable
        service.submit(r);
        service.submit(r);
        // Note: after the submit method is called, the program does not terminate because the thread pool controls the closing of threads.
        // The used thread is returned to the thread pool
        // Close thread pool
        //service.shutdown();
    }
}

Chapter 3 Lambda expression

3.1 overview of functional programming ideas

[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-wsekhqvp-16209583529) (IMG / 03-overview. PNG)]

In mathematics, a function is a set of calculation schemes with input and output, that is, "what to do with what". Relatively speaking, object-oriented overemphasizes that "things must be done in the form of objects", while functional thinking tries to ignore the complex object-oriented syntax - emphasizing what to do, not in what form.

Object oriented thinking:

Do a thing, find an object that can solve it, call the method of the object, and complete the thing

Functional programming idea:

As long as the results can be obtained, it doesn't matter who does it or how to do it. What we pay attention to is the results, not the process

3.2 redundant Runnable code

Traditional writing

When a thread needs to be started to complete a task, it is usually through Java Lang. runnable interface to define the task content, and use Java Lang. thread class to start the thread. The code is as follows:

public class Demo01Runnable {
	public static void main(String[] args) {
    	// Anonymous Inner Class 
		Runnable task = new Runnable() {
			@Override
			public void run() { // Override abstract method
				System.out.println("Multithreaded task execution!");
			}
		};
		new Thread(task).start(); // Start thread
	}
}

Based on the idea of "everything is an object", this approach is understandable: first create an anonymous internal class object of Runnable interface to specify the task content, and then hand it over to a thread to start.

code analysis

For the anonymous inner class usage of Runnable, several points can be analyzed:

  • The Thread class requires the Runnable interface as a parameter, and the abstract run method is used to specify the core of the Thread task content;
  • In order to specify the method body of run, we have to need the implementation class of Runnable interface;
  • In order to save the trouble of defining a RunnableImpl implementation class, anonymous inner classes have to be used;
  • The abstract run method must be overwritten and rewritten, so the method name, method parameters and method return value have to be written again and can not be written wrong;
  • In fact, it seems that only the method body is the key.

3.3 conversion of programming ideas

What to do, not how to do it

Do we really want to create an anonymous inner class object? no We just have to create an object to do this. What we really want to do is pass the code in the run method body to the Thread class.

Pass a piece of code -- that's our real purpose. Creating objects is only a means that has to be taken due to the limitation of object-oriented syntax. So, is there a simpler way? If we return our focus from "how to do" to "what to do", we will find that as long as we can better achieve the goal, the process and form are not important.

Life examples

[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-petczv5p-16209583532) (IMG / 01 - mode of transportation. png)]

When we need to travel from Beijing to Shanghai, we can choose high-speed rail, car, cycling or hiking. Our real goal is to get to Shanghai, and the form of how to get to Shanghai is not important, so we have been exploring whether there is a better way than high-speed rail - by plane.

[the external chain picture transfer fails. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-pxkohtvu-16209583535) (IMG / 02 lambda. PNG)]

Now this kind of aircraft (or even spacecraft) has been born: in March 2014, Java 8 (JDK 1.8) released by Oracle added the heavyweight new feature of Lambda expression, opening the door to a new world for us.

3.4 experience the better writing method of Lambda

With the help of the new syntax of Java 8, the anonymous internal class writing method of the above Runnable interface can be equivalent through a simpler Lambda expression:

public class Demo02LambdaRunnable {
	public static void main(String[] args) {
		new Thread(() -> System.out.println("Multithreaded task execution!")).start(); // Start thread
	}
}

The execution effect of this code is exactly the same as that just now. It can be passed at the compilation level of 1.8 or higher. From the semantics of the code, we can see that we start a thread, and the content of the thread task is specified in a more concise form.

There is no longer the constraint of "having to create interface objects", and there is no longer the burden of "abstract method override", which is so simple!

3.5 review anonymous inner classes

How does Lambda beat object-oriented? In the above example, the core code is just as follows:

() -> System.out.println("Multithreaded task execution!")

In order to understand the semantics of Lambda, we need to start with traditional code.

Using implementation classes

To start a Thread, you need to create an object of the Thread class and call the start method. In order to call the method of Thread, the Thread needs to be constructed:

  • public Thread(Runnable target)

In order to obtain the implementation object of the Runnable interface, you can define an implementation class RunnableImpl for the interface:

public class RunnableImpl implements Runnable {
	@Override
	public void run() {
		System.out.println("Multithreaded task execution!");
	}
}

Then create the object of this implementation class as the construction parameter of Thread class:

public class Demo03ThreadInitParam {
	public static void main(String[] args) {
		Runnable task = new RunnableImpl();
		new Thread(task).start();
	}
}

Use anonymous inner classes

This RunnableImpl class exists only to implement the Runnable interface and has only been used once. Therefore, using the syntax of anonymous inner class can omit the separate definition of this class, that is, anonymous inner class:

public class Demo04ThreadNameless {
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Multithreaded task execution!");
			}
		}).start();
	}
}

Advantages and disadvantages of anonymous inner classes

On the one hand, anonymous inner classes can help us save the definition of implementation classes; On the other hand, the syntax of anonymous inner classes -- it's really too complicated!

semantic analysis

After careful analysis of the semantics in the code, the Runnable interface has only one definition of run method:

  • public abstract void run();

That is, a scheme for doing things is formulated (actually a function):

  • No parameters: the scheme can be executed without any conditions.
  • No return value: the scheme does not produce any results.
  • Code block (method): the specific implementation steps of the scheme.

The same semantics is embodied in Lambda grammar, which should be simpler:

() -> System.out.println("Multithreaded task execution!")
  • The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
  • An arrow in the middle represents passing the previous parameter to the following code;
  • The following output statement is the business logic code.

3.6 Lambda standard format

Lambda omits the object-oriented rules and regulations, and the format consists of three parts:

  • Some parameters
  • An arrow
  • A piece of code

The standard format of Lambda expression is:

(Parameter type parameter name) -> { Code statement }

Format Description:

  • The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
  • ->Is a newly introduced syntax format, which represents pointing action.
  • The syntax in braces is basically consistent with the requirements of traditional method body.

3.7 exercise: use Lambda standard format (no parameter, no return)

subject

Given a Cook interface, it contains the only abstract method makeFood, with no parameters and no return value. As follows:

public interface Cook {
    void makeFood();
}

In the following code, please use Lambda's standard format to call the invokeCook method and print out "eat!" word:

public class Demo05InvokeCook {
    public static void main(String[] args) {
        // TODO, please use Lambda [standard format] to call invokeCook method here
    }

    private static void invokeCook(Cook cook) {
        cook.makeFood();
    }
}

answer

public static void main(String[] args) {
    invokeCook(() -> {
      	System.out.println("time to have a meal!");
    });
}

Note: parentheses represent that the parameter of the Cook interface makeFood abstract method is empty, and braces represent the method body of makeFood.

3.8 Lambda parameters and return values

demand:
    Use an array to store multiple Person object
    For in the array Person Object use Arrays of sort Methods ascending order by age

The following example demonstrates Java util. Usage scenario code of comparator < T > interface, in which the abstract method is defined as:

  • public abstract int compare(T o1, T o2);

When you need to sort an array of objects, arrays The sort method requires a Comparator interface instance to specify the sorting rules. Suppose there is a Person class with two member variables: String name and int age:

public class Person { 
    private String name;
    private int age;
    
    // Omit constructor, toString method and Getter Setter 
}

Traditional writing

If the traditional code is used to sort the Person [] array, it is written as follows:

import java.util.Arrays;
import java.util.Comparator;

public class Demo06Comparator {
    public static void main(String[] args) {
      	// An array of objects that are out of order
        Person[] array = {
        	new Person("Gulinaza", 19),
        	new Person("Delireba", 18),
       		new Person("Marzaha", 20) };

      	// Anonymous Inner Class 
        Comparator<Person> comp = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };
        Arrays.sort(array, comp); // The second parameter is the collation, that is, the Comparator interface instance

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

This approach seems to be "taken for granted" in the idea of object-oriented. The instance of Comparator interface (using anonymous inner class) represents the sorting rule of "from small to large by age".

code analysis

Now let's find out what the above code really wants to do.

  • For sorting, arrays Sort method needs sorting rules, that is, the instance of Comparator interface, and the abstract method compare is the key;
  • In order to specify the method body of compare, we have to need the implementation class of Comparator interface;
  • In order to save the trouble of defining a ComparatorImpl implementation class, anonymous inner classes have to be used;
  • The abstract compare method must be overwritten and rewritten, so the method name, method parameters and method return value must be written again without error;
  • In fact, only parameters and method bodies are critical.

Lambda writing

import java.util.Arrays;

public class Demo07ComparatorLambda {
    public static void main(String[] args) {
        Person[] array = {
          	new Person("Gulinaza", 19),
          	new Person("Delireba", 18),
          	new Person("Marzaha", 20) };

        Arrays.sort(array, (Person a, Person b) -> {
          	return a.getAge() - b.getAge();
        });

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

3.9 exercise: use Lambda standard format (with reference and return)

subject

Given a Calculator interface with an abstract method calc, you can add two int numbers to get the sum value:

public interface Calculator {
    int calc(int a, int b);
}

In the following code, please use Lambda's standard format to call invokeCalc method to complete the addition calculation of 120 and 130:

public class Demo08InvokeCalc {
    public static void main(String[] args) {
        // TODO, please use Lambda [standard format] to call invokeCalc method here to calculate the result of 120 + 130 ß
    }

    private static void invokeCalc(int a, int b, Calculator calculator) {
        int result = calculator.calc(a, b);
        System.out.println("The result is:" + result);
    }
}

answer

public static void main(String[] args) {
    invokeCalc(120, 130, (int a, int b) -> {
      	return a + b;
    });
}

Note: parentheses represent the parameters of calc abstract method of Calculator interface, and braces represent the method body of calc.

3.10 Lambda omitted format

It can be deduced and omitted

Lambda emphasizes "what to do" rather than "how to do", so any information that can be deduced from the context can be omitted. For example, the above example can also use lambda's ellipsis:

public static void main(String[] args) {
  	invokeCalc(120, 130, (a, b) -> a + b);
}

Omission rule

On the basis of Lambda standard format, the rules for using ellipsis are as follows:

  1. The types of parameters in parentheses can be omitted;
  2. If there is only one parameter in the parenthesis, the parenthesis can be omitted;
  3. If there is only one statement in braces, you can omit braces, return keyword and statement semicolon regardless of whether there is a return value or not.

Note: after mastering these omission rules, please review the multithreading cases at the beginning of this chapter accordingly.

3.11 exercise: use Lambda to omit the format

subject

Still use the Cook interface with the only makeFood abstract method mentioned above. In the following code, please use Lambda's ellipsis format to call the invokeCook method and print out "eat!" word:

public class Demo09InvokeCook {
    public static void main(String[] args) {
        // TODO, please use Lambda [omit format] to call invokeCook method here
    }

    private static void invokeCook(Cook cook) {
        cook.makeFood();
    }
}

answer

public static void main(String[] args) {
  	invokeCook(() -> System.out.println("time to have a meal!"));
}

3.12 premise of lambda

Lambda's syntax is very concise, completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:

  1. Lambda must have an interface, and there must be only one abstract method in the interface.
    Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, Lambda can be used only when the abstract methods in the interface exist and are unique.
  2. Using Lambda must have context inference.
    That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda before Lambda can be used as an instance of the interface.

Note: an interface with only one abstract method is called "functional interface".

Keywords: Java

Added by jiggens on Thu, 10 Feb 2022 05:11:11 +0200