Threads execute 8 methods in order



1, Foreword

This paper uses 8 methods to realize the method of making threads run sequentially in multithreading, which involves many common methods in multithreading, not only to know how to make threads run sequentially, but also to let readers have a deeper understanding of the use of multithreading. The methods used are as follows:

  1. join method using thread
  2. Use the join method of the main thread
  3. wait method using thread
  4. Thread pool method using threads
  5. Use the thread's condition method
  6. Use the countdownlatch method of the thread
  7. Cyclicbarrier method using thread
  8. Semaphore method using thread

2, Realize

We need to complete the following application scenario:

1. Morning; 2. Testers, product managers and developers come to work in the company one after another; 3. Product manager plans new requirements; 4. Developers develop new requirements and functions; 5. Testers test new functions.

Planning requirements, developing new functions and testing new functions are sequential. We regard thread1 as a product manager, thread2 as a developer and thread3 as a tester.



1. Use thread join method

Join(): This is Theard's method. The calling thread needs to wait for the join() thread to finish executing before it can continue running.

Application scenario: when a thread has to wait for another thread to execute, you can use the join method.

package demo.order.thread;

public class ThreadJoinMain {

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Product manager plans new requirements");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                    System.out.println("Developers develop new requirements and functions");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                    System.out.println("Testers test new features");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

  Operation results

  2. Use the join method of the main thread

  Here is to use join() in the main thread to block the thread.

package demo.order.thread;

public class MainThreadJoinMain {
    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The product manager is planning new requirements...");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Developers develop new requirements and functions");
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Testers test new features");
            }
        });

        System.out.println("morning:");
        System.out.println("The product manager is coming to work");
        System.out.println("The testers are coming to work");
        System.out.println("Developers are coming to work");
        thread1.start();
        //After the parent process calls the join() method of the child process, the parent process needs to wait for the child process to run before continuing.
        System.out.println("Developer and tester break...");
        thread1.join();
        System.out.println("Product manager new requirements planning completed!");
        thread2.start();
        System.out.println("Tester rest meeting...");
        thread2.join();
        thread3.start();
    }
}

  Operation results  

  3. wait method using thread

Wait(): a method of Object. It is used to make the current thread enter the waiting state. At the same time, wait() will also make the current thread release the lock it holds. "Until other threads call the notify() method or notifyAll() method of this Object", the current thread is awakened (enters the "ready state")

notify() and notifyAll(): Object methods that wake up the waiting thread on the current Object; Notify () wakes up a single thread, while notifyAll () wakes up all threads.

wait(long timeout): keep the current thread in the "wait (blocking)" state. "Until other threads call the notify() method or notifyAll() method of this object, or the specified amount of time is exceeded, the current thread will be awakened (enter the" ready state ").

Application scenario: how to implement producers and consumers in Java.

package demo.order.thread;

public class ThreadWaitMain {
    private static Object myLock1 = new Object();
    private static Object myLock2 = new Object();

    /**
     * Why add these two identification States?
     * If there is no status identification, t2 runs only after t1 has run. t2 waits for t1 to wake up, resulting in t2 always in the waiting state
     */
    private static Boolean t1Run = false;
    private static Boolean t2Run = false;

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1) {
                    System.out.println("Product manager plans new requirements...");
                    t1Run = true;
                    myLock1.notify();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1) {
                    try {
                        if (!t1Run) {
                            System.out.println("Developers take a break first...");
                            myLock1.wait();
                        }
                        synchronized (myLock2) {
                            System.out.println("Developers develop new requirements and functions");
                            myLock2.notify();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock2) {
                    try {
                        if (!t2Run) {
                            System.out.println("The tester will have a rest first...");
                            myLock2.wait();
                        }
                        System.out.println("Testers test new features");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

   Operation results  

  4. Thread pool method using threads

  JAVA provides four thread pools through Executors

  • New SingleThread executor;
  • Controllable maximum concurrent number thread pool (newFixedThreadPool);
  • Recyclable cache thread pool (newCachedThreadPool);
  • Support thread pool for timed and periodic tasks (newScheduledThreadPool).

New single thread executor: the advantage is that all tasks are executed serially.

submit(): submit the task.

shutdown(): this method is used to close the thread pool and reject new tasks.

Application scenario: serial execution of all tasks. If the only thread ends abnormally, a new thread will replace it. This thread pool ensures that all tasks are executed in the order they are submitted.

package demo.order.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolMain {
    static ExecutorService executorService = Executors.newSingleThreadExecutor();

    public static void main(String[] args) throws Exception {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Product manager plans new requirements");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Developers develop new requirements and functions");
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Testers test new features");
            }
        });

        System.out.println("morning:");
        System.out.println("The product manager is coming to work");
        System.out.println("The testers are coming to work");
        System.out.println("Developers are coming to work");
        System.out.println("Leadership orders:");
        System.out.println("First, the product manager plans new requirements...");
        executorService.submit(thread1);
        System.out.println("Then, developers develop new requirements and functions...");
        executorService.submit(thread2);
        System.out.println("Finally, the tester tests the new functionality...");
        executorService.submit(thread3);
        executorService.shutdown();
    }
}

    Operation results  

  4. Use the condition method of the thread

  Condition   [k ə n ˈ d ɪʃ n] (condition variable): usually associated with a lock. When you need to share a lock among multiple contidions, you can pass a Lock/RLock instance to the constructor, otherwise it will generate an RLock instance itself.

  • The await() method in Condition is similar to the wait() method in the Object class.

  • The await(long time,TimeUnit unit) method in Condition is similar to the wait(long time) method in the Object class.

  • The signal() method in Condition is similar to the notify() method in the Object class.

  • The signalAll() method in Condition is similar to the notifyAll() method in the Object class.

Application scenario: Condition is a tool class for coordination and communication among multiple threads, which makes one or some threads wait for a Condition together. Only when the Condition is met (the signal or signalAll method is called), these waiting threads will be awakened to re compete for the lock.

package demo.order.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadConditionMain {
    private static Lock lock = new ReentrantLock();
    private static Condition condition1 = lock.newCondition();
    private static Condition condition2 = lock.newCondition();

    /**
     * Why add these two identification States?
     * If there is no status identification, t2 runs only after t1 has run. t2 waits for t1 to wake up, resulting in t2 always in the waiting state
     */
    private static Boolean t1Run = false;
    private static Boolean t2Run = false;

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("Product manager plans new requirements");
                t1Run = true;
                condition1.signal();
                lock.unlock();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if (!t1Run) {
                        System.out.println("Developers take a break first...");
                        condition1.await();
                    }
                    System.out.println("Developers develop new requirements and functions");
                    t2Run = true;
                    condition2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.unlock();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if (!t2Run) {
                        System.out.println("The tester will have a rest first...");
                        condition2.await();
                    }
                    System.out.println("Testers test new features");
                    lock.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

  Running result: there are many kinds of output sequences here, mainly because of the sequence of threads entering, resulting in inconsistent sequence of locking threads

Operation results  

  6. Use the CountDownLatch method of the thread

CountDownLatch    [ka ʊ nt   da ʊ n   læt ʃ] : It is located in the java.util.concurrent package. It can be used to realize functions similar to counters.

Application scenario: for example, there is a task C, which can only be executed after other tasks a and B are executed. At this time, CountDownLatch can be used to realize this function.

package demo.order.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadCountDownLatchMain {
    /**
     * It is used to judge whether thread 1 is executed. The countdown is set to 1 and minus 1 after execution
     */
    private static CountDownLatch c1 = new CountDownLatch(1);

    /**
     * It is used to judge whether thread 2 is executed. The countdown is set to 1 and minus 1 after execution
     */
    private static CountDownLatch c2 = new CountDownLatch(1);

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Product manager plans new requirements");
                //Countdown to c1 - 1
                c1.countDown();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //Wait for c1 countdown. If the countdown is 0, run down
                    c1.await();
                    System.out.println("Developers develop new requirements and functions");
                    //Countdown to c2 - 1
                    c2.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //Wait for c2 countdown. If the countdown is 0, run down
                    c2.await();
                    System.out.println("Testers test new features");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

  Operation results  

  7. Use cyclicbarrier to realize that threads run in sequence

  CyclicBarrier    [ ˈ sa ɪ kl ɪ k    ˈ bæri ə (r)]  : Through it, a group of threads can wait to a certain state and then execute all at the same time. It is called loopback because the CyclicBarrier can be reused after all waiting threads are released. Let's call this state barrier for the moment. When await() method is called, the thread will be in barrier.

Application scenario: the company organizes a spring outing and waits for all employees to arrive at the assembly place before they can start. After everyone arrives, they enter the barrier state. When they all arrived, we started to travel together.

package demo.order.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierMain {
    static CyclicBarrier barrier1 = new CyclicBarrier(2);
    static CyclicBarrier barrier2 = new CyclicBarrier(2);

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Product manager plans new requirements");
                    //Release the fence 1
                    barrier1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //Release the fence 1
                    barrier1.await();
                    System.out.println("Developers develop new requirements and functions");
                    //Release the fence 2
                    barrier2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //Release the fence 2
                    barrier2.await();
                    System.out.println("Testers test new features");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

   Operation results  

  8. Use sephmore (semaphore) to realize that threads run in sequence

Sephmore (Semaphore)   [ ˈ sem ə f ɔː (r) ]: Semaphore is a counting Semaphore. Conceptually, Semaphore contains a set of licenses. If necessary, each acquire() method will block until an available license is obtained. Each release() method will release the line holding the license and return an available license to Semaphore. However, in fact, there is no real license object for threads to use, and Semaphore only manages and maintains the amount available.

acquire()    [ əˈ kwa ɪə (r) ]: the current thread attempts to obtain 1 license. This process is blocked. If the current thread obtains 1 available license, it will stop waiting and continue to execute.

release()     [r ɪˈ li ː s]  : The current thread releases 1 available license.

Application scenario: Semaphore can be used for traffic diversion, especially for scenarios with limited public resources, such as database connection. Assuming this requirement, read the data of tens of thousands of files into the database. Since file reading is an IO intensive task, dozens of threads can be started to read concurrently, but the number of database connections is only 10. At this time, it must be controlled that only 10 threads can get the database connection for operation. At this time, Semaphore can be used for flow control.

package demo.order.thread;

import java.util.concurrent.Semaphore;

public class SemaphoreMain {
    private static Semaphore semaphore1 = new Semaphore(1);
    private static Semaphore semaphore2 = new Semaphore(1);

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Product manager plans new requirements");
                semaphore1.release();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore1.acquire();
                    System.out.println("Developers develop new requirements and functions");
                    semaphore2.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore2.acquire();
                    thread2.join();
                    semaphore2.release();
                    System.out.println("Testers test new features");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("morning:");
        System.out.println("The testers are coming to work...");
        thread3.start();
        System.out.println("The product manager is coming to work...");
        thread1.start();
        System.out.println("Developers are coming to work...");
        thread2.start();
    }
}

   Operation results  


summary

The condition for sequential operation is that all threads are in the running state. Here, the three threads cannot be guaranteed to enter the running state at the same time. We can use CountDownLatch to make all three threads enter the running state and then execute the business code, so that there will be no non sequential operation.

JAVA multithreading -- atomicity, orderliness and visibility of thread safety_ Blog of a60782885 - CSDN blog_ Multithreading atomicity

Multithreading -- ordering_ The blog of the youth carrying sacks - CSDN blog

In depth understanding of Java multithreading and concurrency box (part ③) -- Java Memory Model and atomicity, visibility and ordering - software technology - billion speed cloud

Keywords: Java Multithreading thread pool

Added by rapbhan on Sat, 02 Oct 2021 04:20:33 +0300