Java learning 07 multithreading

Keywords: Java Back-end

Multithreading

1, Process and Thread

  • A program is an ordered collection of instructions and data. It has no running meaning and is static

  • A program in process execution is a dynamic concept. It is the unit of system resource allocation

  • Usually, a process can contain multiple threads. There is at least one thread in a process. Thread is the unit of CPU scheduling and execution

    For example, a program can contain sound, images, subtitles, etc

  • be careful:

    Many multithreads are simulated. Real multithreading has multiple CPUs, that is, multi-core, such as servers. If the simulated multithreading, that is, under one CPU, the CPU can only execute one code at the same time point, because the switching is fast, there will be the illusion of simultaneous execution.

1.1 core concepts

  • Threads are independent execution paths
  • When the program is running, even if no thread is created, there will be multiple threads, such as main thread and gc thread
  • main() is called the main thread, which is the system entry and executes the whole program
  • If multiple threads are opened in a process, the operation of threads is scheduled by * * scheduler (CPU) * *. The scheduler is closely related to the operating system, and the sequence cannot be interfered by human beings
  • When operating on the same resource, there will be a problem of resource grabbing, and concurrency control needs to be added (only one instruction can be executed at a time)
  • Threads will bring additional overhead, such as CPU scheduling time and concurrency control overhead
  • Each thread interacts in its own working memory. Improper memory control will cause data inconsistency

2, Three creation methods

2.1. Inherit Thread class

  • The custom Thread class inherits the Thread class
  • Rewrite the * * run() * * method to write the thread execution body
  • Create a thread object and start the thread using the * * start() * * method

When the run method is called, there is only one execution path for the main thread, and the run method returns to the main path after execution

Call the start method, multiple execution paths, and the main thread and sub thread execute alternately in parallel. Each execution order may be different, and the execution is scheduled by the CPU

2.2. Implement Runnable interface

(recommended because of the limitations of Java single inheritance)

  • Define a class to implement the Runnable interface
  • Implement the * * run() * * method and write the thread execution body
  • Create an object that implements the Runnable interface
  • Create a thread object proxy, put it into the Runnable object, and start it with start()
public class B_Runnable implements Runnable{
    @Override
    public void run() {
        //run method thread body
        for (int i = 0; i < 20; i++) {
            System.out.println("Branch thread"+i);
        }
    }

    public static void main(String[] args) {
        //main thread

        //Create runnable interface implementation class object
        B_Runnable runnable = new B_Runnable();
        //Create a thread object and start the thread agent through the thread object
        Thread thread = new Thread(runnable);
        thread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("Main thread"+i);
        }
    }
}

2.3. Implement Callable interface

  1. To implement the Callable interface, the return value type is required
  2. When overriding the call method, you need to throw an exception
  3. Create target object
  4. Create execution service: ExecutorService ser = Executors.newFixedThreadPool(1);
  5. Submit execution: futrue < Boolean > result1 = Ser. Submit (T1);
  6. Get result: boolean r1 = result1.get();
  7. Shut down the service: ser.shutdownNow();
public class A_Callable implements Callable {
    private String url;
    private String name;

    public A_Callable(String url,String name){
        this.url = url;
        this.name = name;

    }
    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("Downloaded files"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        A_Callable t1 = new A_Callable("https://www.baidu.com/img/PC_7ac6a6d319ba4ae29b38e5e4280e9122.png "," Baidu. png ");
        A_Callable t2 = new A_Callable("https://static.baydn.com/static/img/logo_v4.png "," scallop. png ");
        A_Callable t3 = new A_Callable("https://egame.gtimg.cn/club/pgg_pcweb/v2/img/befe0d6.png?max_age=31536000 "," penguin. PNG ");

        //Create execution service
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //Submit for execution
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        //Get results
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        //Shut down service
        ser.shutdownNow();


    }

}


//Downloader
class WebDownloader{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO Abnormal, downLoader There is a problem with the method");
        }

    }
}

2.4 static agent

  1. Real objects and proxy objects should implement the same interface
  2. The proxy object is really a role, so you need to pass in the target object

Review Thread and Runnable

  • Thread is a proxy. The real object is the implementation class of Runnable interface. Both thread and Runnable implement Runnable interface. They both have a common run method.
  • In the Thread, judge whether the target object is empty. If not, execute the run method of the target object by target.run
public class A_Static {
    public static void main(String[] args) {
        WeddingCompany company = new WeddingCompany(new You());
        company.HappyMarry();
    }

}

interface Marry{
    //Four happy events
    void HappyMarry();
}

//Real object
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("Darwin get marry");
    }
}

//agent
class WeddingCompany implements Marry{

    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }
    @Override
    public void HappyMarry() {
        this.target.HappyMarry();   //Agent execution HappyMarry
    }
}

2.5. Lamda expression

  • It is essentially functional programming
    • Avoid too many internal class definitions
    • Remove meaningless code and leave the core logic

Functional interface

  • Understanding the Functional Interface is the key to learning java 8 lamda expressions
  • Definition of functional interface
    • Any interface that contains only one abstract method is a functional interface
    • For functional interfaces, we can create the interface object through lamda expressions.
/*
Derivation of Lamda expression
 */
public class A_Lamda {

    //3. Static internal class
    static class Like2 implements ILike{
        @Override
        public void lamda() {
            System.out.println("I LIke Lamda2");
        }
    }


    public static void main(String[] args) {
        ILike like = new Like();
        like.lamda();

         like = new Like2();
         like.lamda();

         //4. Local internal class
        class Like3 implements ILike{
            @Override
            public void lamda() {
                System.out.println("I LIke Lamda3");
            }
        }

        like = new Like3();
        like.lamda();

        //5. Anonymous internal class, without class name, must use interface or parent class
        like = new ILike(){
            @Override
            public void lamda() {
                System.out.println("I LIke Lamda4");
            }
        };
        like.lamda();


        //6. Simplify with Lambda
        like = ()-> {
            System.out.println("I LIke Lamda5");
        };

        like.lamda();


    }
}

//1. Define a functional interface
interface ILike{
    void lamda();

}

//2. Implementation class
class Like implements ILike{
    @Override
    public void lamda() {
        System.out.println("I LIke Lamda");
    }
}

Summary

  1. Lambda expressions can only be reduced to one line if there is only one line of code, otherwise they are wrapped in curly braces
  2. Using Lambda expressions, the interface must be functional, that is, there is only one method
public class A_Lamda02 {


    public static void main(String[] args) {
\
        //1.Lambda expression simplification
        ILove love = (int a) ->{
                System.out.println("I love you "+a);
            };
        //Simplified 1. Parameter type
        love = (a) ->{
            System.out.println("I love you "+a);
        };
        //Simplified 2. Parentheses
        love = a ->{
            System.out.println("I love you "+a);
        };
        //Simplified 3. Curly braces
        love = a -> System.out.println("I love you "+a);
        love.love(520);
    }
}

interface ILove{
    void love(int a);
}

3, Thread state

3.1 thread status

3.2 thread method

3.3. Thread Stop

  • The stop() and destroy() methods provided by JDK are not recommended

  • It is recommended to let the thread stop by itself

  • Terminate the variable through the flag bit. When flag = false, terminate the thread

    //Test stop
    //1. It is recommended that the thread stop -- > utilization times normally and do not loop
    //2. It is recommended to use flag bit -- > by setting a flag bit
    //3. Do not use outdated methods such as stop or destroy, which are not recommended by Jdk
    public class A_Stop implements Runnable {
    
        //Define a flag bit
        private boolean flag = true;
    
        @Override
        public void run() {
            int i = 0;
            while(flag){
                System.out.println("run Thread"+i++);
            }
    
        }
    
        //Set an open method to stop the thread and convert the flag bit
        public void stop(){
            this.flag = false;
        }
    
        public static void main(String[] args) {
            A_Stop stop = new A_Stop();
    
            new Thread(stop).start();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("main"+i);
                if(i == 900){
                    stop.stop();
                    System.out.println("The thread should stop");
                }
    
            }
    
    
        }
    }
    

3.4 thread Sleep

  • Thread.sleep();

  • Sleep (time) specifies the number of milliseconds that the current thread blocks;

  • Exception InterruptException in sleep

  • When the sleep time reaches, the thread enters the ready state

  • sleep can simulate network delay, countdown, etc

  • Each object has a lock, and sleep does not release the lock

//Analog countdown
public static void countDown() throws InterruptedException {
    int num  = 10;

    while (true){
        Thread.sleep(1000);
        System.out.println(num--);
        if(num <= 0){
            break;
        }
    }


}
//Print current system time
Date date = new Date(System.currentTimeMillis());//Get the current system time
while (true){
    try {
        Thread.sleep(1000);
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
         date = new Date(System.currentTimeMillis());//Update time
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

3.4. Thread comity (Yield)

  • Comity thread, which allows the currently executing thread to pause temporarily without blocking

  • The running state of the thread changes to the ready state

  • Let the CPU reschedule, but not necessarily successful. It depends on the CPU mood

  • If the output result is a - > b - > A - > b, it is successful

    //Field test
    public class B_Yield {
        public static void main(String[] args) {
            MyYield myYield = new MyYield();
    
            new Thread(myYield,"A").start();
            new Thread(myYield,"B").start();
    
        }
    }
    
    class MyYield implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"Start");
            Thread.yield();//Comity
            System.out.println(Thread.currentThread().getName()+"Stop");
        }
    }
    

3.5. Merge threads (Join)

  • Join merge threads. After this thread is executed, execute other threads. Other threads are blocked

  • It can be imagined as queue jumping. In the following code, at first, the main thread runs alternately with other threads and randomly allocates memory. When the main thread reaches 200, the Join thread inserts until the run is completed

    //Test Join
    public class C_Join implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i <= 1000; i++) {
                System.out.println("Vip Here comes the thread"+i);
    
            }
        }
    
        public static void main(String[] args) {
            C_Join join = new C_Join();
            Thread thread = new Thread(join);
            thread.start();
    
            //Main thread
            for (int i = 0; i < 500; i++) {
                if (i == 200) {
                    try {
                        thread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                    System.out.println("main"+ i);
                }
            }
    
        }
    

3.6 thread state observation

  • Thread.state

    Thread state. A thread may be in one of the following states

    1. NEW

      New state, thread not started

    2. RUNNABLE

      The thread executing in the Java virtual machine is in this state

    3. BLOCKED

      Threads that are blocked waiting for a monitor lock are in this state

    4. WAITING

      A thread waiting for another thread to perform a specific action is in this state

    5. TIMED_WAITING

      The thread is in this state when it waits for another thread to perform an action to reach the specified wait

    6. TERMINATED

      The exited thread is in this state

    public class D_TestState {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000); //Refresh the status every second
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread "+i);
                }
                System.out.println("");
            });
    
            //Observation state
            Thread.State state = thread.getState();
            System.out.println(state);
    
            //Observe the status after startup
            thread.start();
            state = thread.getState();
            System.out.println(state);
    
            while(state != Thread.State.TERMINATED){//As long as the thread does not terminate, it always outputs the state
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                state = thread.getState();
                System.out.println(state);
    
            }
        }
    }
    

3.8 thread Priority

  • Java provides a thread scheduler to monitor all threads that enter the ready state after startup. The thread scheduler determines which thread should be scheduled to execute according to priority

  • The priority of threads is expressed in numbers, ranging from 1 to 10

    1. Thread.MIN_PRIORITY = 1;
    2. Thread.NORM_PRIORITY = 5;
    3. Thread.MAX_PRIORITY = 10;
  • Change or get priority in the following ways

    getPriority().setPriority(int xxx);

    public class E_Priority {
    
        public static void main(String[] args){
            System.out.println(Thread.currentThread().getName()+"Your priority is"+Thread.currentThread().getPriority());
    
            MyPriority my = new MyPriority();
    
            Thread t1 = new Thread(my);
            Thread t2 = new Thread(my);
            Thread t3 = new Thread(my);
            Thread t4 = new Thread(my);
            Thread t5 = new Thread(my);
    
            //Priority startup not set
            t1.start();
    
            t2.setPriority(1);
            t2.start();
    
            t3.setPriority(4);
            t3.start();
    
            t4.setPriority(Thread.MAX_PRIORITY);
            t4.start();
    
            t5.setPriority(Thread.MIN_PRIORITY);
            t5.start();
        }
    
    
    
    }
    
    class MyPriority implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"Your priority is"+Thread.currentThread().getPriority());
        }
    }
    
  • However, it is not the high priority that runs first. The low thread priority is just the low probability of scheduling. It is not the low thread that must be called finally. Look at the CPU mood (performance inversion)

3.9 daemon

  • Threads are divided into user threads and daemon threads

  • The virtual machine must ensure that the user thread has completed execution

  • The virtual machine does not have to wait for the daemon thread to finish executing

  • For example, the background records operation logs, monitors memory, garbage collection, etc

    //Test daemon thread
    //Test case: God protects you
    public class F_Daemon {
        public static void main(String[] args) {
            God god = new God();
            You you = new You();
    
            Thread thread = new Thread(god);
            thread.setDaemon(true);//The default is false, and the default is user thread
    
            thread.start();
    
            new Thread(you).start();
        }
    
    }
    
    //
    class God implements Runnable{
        @Override
        public void run() {
            while (true){
                System.out.println("God bless you");
            }
        }
    }
    
    
    
    //you
    class You implements Runnable{
        @Override
        public void run() {
            for (int i = 1; i < 36500; i++) {
                System.out.println("Live happily"+i+"day");
            }
            System.out.println("========GoodBye World!=======");
        }
    }
    

4, Thread synchronization

4.1 concurrency

  • Concurrency: multiple threads operate on the same resource at the same time
  • Common situation: tens of thousands of people grab tickets and withdraw money at the same time

4.2 thread synchronization

  • In real life, we will encounter the problems of "one resource" and "many people want to use it". For example, cooking and queuing are the best solutions
  • When dealing with multithreading, multiple threads access the same resource, and some threads want to modify this object, we need thread synchronization.
  • Thread synchronization is a waiting mechanism. Multiple threads that need to access this object at the same time enter the object waiting pool to form a queue, wait for the previous thread to use it, and then use it for the next thread

4.3 queues and locks

  • Because multiple objects in the same process share a piece of storage space, it not only brings convenience, but also brings the problem of access conflict. In order to ensure the correctness of data access in the method, the lock mechanism synchronized is added to the access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use

    • Holding a lock by one thread will cause all other threads that need the lock to hang

    • In multi-threaded competition, locking and releasing locks will lead to more context switching, scheduling delay and performance problems

    • If a high priority thread waits for a low priority thread to release the lock, it will cause priority inversion and performance problems

4.4 synchronization method

  • Because the data object protected by private keyword can only be accessed by methods, the synchronized keyword is proposed for methods, including two methods: synchronized method and synchronized block

    Synchronization method: public synchronized void method(int args){}
    
  • The synchronized method controls the access of "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock. The lock can not be released until the method returns, and the subsequent thread can continue to execute the lock alive

    Defect: declaring a large method synchronized will affect efficiency

4.5 disadvantages of synchronization method

  • Methods only need locks to modify resources. Too many locks waste resources

4.6 synchronization block

  • Synchronization block: synchronized(Obj) {}
  • Obj calls it a synchronization monitor
    • Obj can be any object, but it is recommended to use shared resources as synchronization monitors
    • There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is the object itself this, or the class [explain in reflection], that is, after the synchronized keyword is added to the method in the class, the object is locked and only the current thread can access it
  • Synchronization monitor execution
    1. The first thread accesses, locks the synchronization monitor, and executes the code in it
    2. The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
    3. After the first thread is accessed, unlock the synchronization monitor
    4. The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses
 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        for (int i = 0; i < 10000; i++) {
            new Thread(() ->{
                synchronized (list){
                list.add(Thread.currentThread().getName());
            }}).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

4.7 deadlock

  • Multithreads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. Deadlock may occur when a synchronization block has locks on more than two objects at the same time
  • Multiple threads hold the resources needed by each other to form a stalemate
//Deadlock: multiple threads hold each other's required resources to form a deadlock
public class A_DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"Snow White");
        Makeup g2 = new Makeup(1,"Cinderella");

        g1.start();
        g2.start();
    }
}

//Lipstick
class Lipstick{

}

//mirror
class Mirror{

}

class Makeup extends Thread{
    //Only one resource is needed, which is guaranteed by static
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choose;
    String girlName;

    public Makeup(int choose, String girlName) {
        this.choose = choose;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        makeup();
    }

    //Hold each other's locks
    private void makeup() {
        if (choose == 0) {
            synchronized (lipstick) { //Got the lipstick lock
                System.out.println(this.girlName + "Take lipstick");
                try {
                    Thread.sleep(1000); //Take the mirror in a second
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(this.girlName + "Take the mirror");

            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "Take the mirror");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                    System.out.println(this.girlName + "Take lipstick");

            }
        }
    }
}

4.8 deadlock avoidance method

  • Necessary conditions for deadlock generation
    1. Mutex condition: a resource can only be used by a process at a time
    2. Request and hold condition: when a process is blocked by requesting resources, it holds the obtained lock
    3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up
    4. Circular waiting condition: a circular waiting resource relationship is formed between several processes

4.9 Lock

  • Since JDK 5.0, Java has provided a more powerful thread synchronization mechanism -- synchronization is achieved by displaying and defining synchronization Lock objects. Synchronous locks use Lock objects as

  • The java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared object resources. Lock provides independent access to shared resources. Only one thread object can lock the lock object at a time. Before a thread starts to access shared resources, the live lock object should be locked first

  • The ReentranLock class implements Lock. It has the same concurrency mechanism and memory semantics as synchronized. ReentranLock is commonly used in thread safety control, which can display locking and releasing locks.

    //Lock test: case of using unsafe ticket buying
    public class A_Lock {
    
        public static void main(String[] args) {
            TestLock testLock = new TestLock();
    
            new Thread(testLock,"A").start();
            new Thread(testLock,"B").start();
            new Thread(testLock,"C").start();
        }
    }
    
    class TestLock implements Runnable{
    
         int ticketNums = 10;
    
         //Define lock lock
        private final ReentrantLock lock = new ReentrantLock();
        
        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();
                    if (ticketNums > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);
    
                    } else {
                        break;
                    }
                }finally {
                    lock.unlock();
    
                }
    
                }
            }
    
    }
    

4.10 comparison between synchronized and Lock

  • Lock is a display lock (which needs to be opened and closed manually), and synchronized is implicit, except that the scope is automatically released

  • Lock has only code block locks, and synchronized has code block and object locks

  • Using Lock lock, the JVM spends less time to schedule processes, and the performance is better. And it has good extensibility (providing more subclasses)

  • Priority order

    Lock -- > synchronization code block (it has been entered into the method body and corresponding resources have been allocated) – > synchronization method (outside the method body)

5, Thread collaboration

5.1 thread communication

  • Application scenario: producer and consumer issues

    • Suppose that only one product can be stored in the warehouse, the producer puts the produced products into the warehouse, and the consumer takes the products from the warehouse for consumption
    • If there is no product in the warehouse, the producer will put the product into the warehouse, otherwise stop production and wait until the product is taken away by the consumer
    • If there are products in the warehouse, the consumer takes the products and consumes them. Otherwise, stop consuming and wait until the producer puts in the products again

5.2 thread communication - Analysis

This is a problem of thread synchronization. Producers and consumers share the same resource, and producers and consumers are interdependent and conditional on each other.

  • For producers, consumers should be informed to wait before producing products. After the production of products, consumers need to be informed of consumption
  • For consumers, after consumption, they need to inform producers that they have finished consumption and need to produce new products
  • In the producer consumer problem, synchronized is not enough
    • synchronized can prevent concurrent updates of the same resource and achieve synchronization
    • synchronized cannot be used for message passing between different threads

5.3 thread communication

  • Java provides several methods to solve the communication problem between threads

  • Note: all of them are Object class methods, which can only be used in synchronization methods or synchronization code blocks. Otherwise, IlleagalMonitorStateException will be thrown

5.4 solution 1

Concurrent collaboration model "producer / consumer model" -- > management method

  • Producer: responsible for producing data modules (possibly methods, objects, threads, processes)
  • Consumer: responsible for processing data modules (possibly methods, objects, threads, processes)
  • Buffer: consumers cannot directly use the producer's data. There is a buffer between them

The producer puts the produced secondary data into the buffer, and the consumer takes the data from the buffer

//Test: producer consumer model -- > using buffer: pipe process method

public class B_PC {

    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }

}

//producer
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container){
        this.container = container;
    }

    //production
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("Produced"+i+"Chicken");
            container.push(new Chicken(i));
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


//consumer
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container){
        this.container = container;
    }

    //consumption
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("Consumed the second"+container.pop().id+"Chicken");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

//product
class Chicken{
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}


//buffer
class SynContainer{

    //Container size
    Chicken[] chickens = new Chicken[10];
    //Container counter
    int count = 0;

    //The producer puts in the product
    public synchronized void push(Chicken chicken) {
        //If the container is full, wait for the consumer to consume
        if (count == chickens.length) {
            //Inform consumers of consumption, production and waiting
            try {
                System.out.println("Please consume, the chicken is full");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //If not, put in the product
        chickens[count] = chicken;
        count++;

        //Consumers can be notified of consumption
        this.notify();

    }

    //Consumer products
    public synchronized Chicken pop(){
        //Judge whether it can be consumed
        if(count == 0){
            //Waiting for producer production

            try {
                System.out.println("No chicken, please produce");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        Chicken chicken = chickens[count];


        //When finished, inform the producer to produce
        this.notify();
        return chicken;
    }
}

5.5 solution 2

  • Concurrent collaboration model "producer / consumer model" -- > semaphore
  • Judge when to wait and when to wake up
//Test producer consumer problem 2: signal lamp method, flag bit solution
public class C_PC {
    public static void main(String[] args) {

        Program program = new Program();

        new Performer(program).start();
        new Audiance(program).start();
    }
}


//Producer -- > actor
class Performer extends Thread{
    Program program;
    public Performer(Program program){
        this.program = program;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.program.perform("The program is playing");
            }else{
                this.program.perform("In the advertisement...");
            }
            
        }
    }
}


//Consumer -- > audience
class Audiance extends Thread{
    Program program;
    public Audiance(Program program){
        this.program = program;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.program.watch();
        }
    }
}


//Products -- > Programs
class Program {

    String voice;
    Boolean flag = false;    //Judge whether there is a program

    //The actors performed and the audience waited
    public synchronized void perform(String voice){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("The actors performed:"+voice);
        //Inform the audience to watch
        this.voice = voice;
        this.notify();
        this.flag = !this.flag;

    }

    //Look at the audience, the actors wait
    public synchronized void watch(){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Watched"+voice);
        //Inform the actors to perform
        this.notifyAll();
        this.flag = !this.flag;
    }
}

5.6 thread pool

  • Background: resources that are often created and destroyed and are used heavily, such as threads in the case of concurrency, have a great impact on performance

  • Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back when used up.

    It can avoid frequent creation, destruction and reuse. Similar means of transportation.

  • Benefits:

    1. Improved response speed (reduced time to create new threads)
    2. Reduce resource consumption (reuse thread pool threads and do not need to be created each time)
    3. Easy thread management
      • corePoolSize: core pool size
      • maximumPoolSize: maximum number of threads
      • keepAliveTime: how long does the thread last when there is no task, and then it will terminate

5.7. Using thread pool

  • JDK 5.0 provides thread pool related APIs: executorservice and Executors
  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor
    • void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable
    • Future submit(Callabletask): execute a task with a return value. It is generally used to execute Callable
    • void shutdown(): closes the connection pool
  • Executor: tool class and thread pool factory class, which are used to create and return different types of thread pools
//Test thread pool
public class D_Pool {
    public static void main(String[] args) {

        //1. Create service and thread pool
        //The newFixedThreadPool parameter is the pool size
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2. Close the connection

        service.shutdown();
    }
}


class MyThread implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

Posted by beezza on Sun, 31 Oct 2021 09:22:55 -0700