java multithreading -- life cycle, thread communication

java life cycle, thread communication

 

1, Life cycle

The thread life cycle depends on the following figure. Around the figure, explain the meaning of its methods and the differences between different methods.

1. yield() method

yield() puts the currently running thread back in place to allow other threads with the same priority to get the chance to run. However, in practice, yield() cannot be guaranteed to achieve the purpose of concession, because the concession thread may be selected again by the thread scheduler.

At the same time, yield() will not give up the lock resource, so there may be deadlock.

2. Difference between wait and sleep methods

1) The first important difference is that the wait method must be used in a synchronous environment, such as synchronized method or synchronized code block. If you do not use it under synchronization conditions, an IllegalMonitorStateException will be thrown. In addition, the sleep method does not need to be called under the condition of resynchronization, and you can use it normally.

2) The second difference is that the wait method is used for and defined in the Object class, while the sleep method operates on the current thread, defined in the java.lang.Thread Class.

3) The third difference is that when wait() is called, the method releases the currently held lock, while the sleep method does not release any locks.

3. Use scenarios of wait and sleep methods

(1) the wait method is defined in the Object class, which can be used by all objects. Generally, the wait() and notify() methods or notifyAll are used for communication between threads.

(2) the sleep() method is used to pause the execution of the current thread.

4. join method ()

          thread.Join By adding the specified thread to the current thread, you can merge two alternate threads into sequential threads.

For example, in thread B, the Join() method of thread A is invoked until thread A is executed, then thread B continues.

Here's a case to think about? It should be that I didn't fully understand the join, so I will review it later.

/*
 * The first puzzle about join is that after t2.join(), I found that it is invalid and also cross output.
 */
class ThreadTesterA implements Runnable {

    private int counter;

    public void run() {
        while (counter <= 10) {
            System.out.print("Counter = " + counter + " ");
            counter++;
        }
        System.out.println();
    }
}
class ThreadTesterB implements Runnable {
    private int i;
    public void run() {
        while (i <= 10) {
            System.out.print("i = " + i + " ");
            i++;
        }
        System.out.println();
    }
}

public class ThreadTester {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new ThreadTesterA());
        Thread t2 = new Thread(new ThreadTesterB());
        t1.start();
        t2.start();
        t2.join(); //invalid
}
}

5. stop method

After the thread is started, it may need to be terminated during running. Java provides only one stop method, but it is not recommended to use this method because it has the following three problems:

1) the stop method is outdated.

In terms of Java coding rules, outdated methods are not recommended

2)stop method will lead to incomplete code logic

Stop method is a kind of "malicious" interrupt. Once stop method is executed, the running thread will be terminated. It is very dangerous whether the thread logic is complete or not

3)stop method will destroy atomic logic

In order to solve the problem of shared resource preemption, multithreading uses the concept of lock to avoid resource out of sync. However, because of this reason, the stop method will bring more trouble. It will discard all locks, resulting in the damage of atomic logic

 

2, Thread communication small case

 

1. How do I get two threads to execute in sequence?

Topic: suppose there are two threads, one is thread A, the other is thread B, and the two threads can print 1-3 numbers in turn. We hope that B will start printing after A has printed all.

Key method: join().

//Topic: suppose there are two threads, one is thread A,The other is threads B,We hope B stay A Start printing after all printing.
public class TestJoin {
    public static void main(String[] args) {        
        demo2(); 
    }
    
    private static void demo2() {
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                printNumber("A");
            }
        });
        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("B Start waiting A");
                try {
                    A.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                printNumber("B");
            }
        });
        B.start();
        A.start();
    }
    
    private static void printNumber(String threadName) {
        int i=0;
        while (i++ < 3) {
            System.out.println(threadName + "print:" + i);
        }
    }
}
/*Operation results
 * B Start waiting for A
 * Aprint:1
 * Aprint:2
 * Aprint:3
 * Bprint:1
 * Bprint:2
 * Bprint:3
 */

 

2. How to make two threads run in an orderly way?

Topic: suppose there are two threads, one is thread A, the other is thread B, and the two threads can print 1-3 numbers in turn. We want A and B to print alternately

Key methods: wait() and notify() or notifyAll()

public class Main {
    int i = 1;   //i and istrue Shared data as multithreaded
    boolean istrue = false;

    public static void main(String[] args) {
        Main main = new Main();
        ThreadA a = new ThreadA(main);
        ThreadB b = new ThreadB(main);
        Thread threada = new Thread(a);
        Thread threadb = new Thread(b);
        threada.start();
        threadb.start();

    }}

class ThreadA implements Runnable {
    Main main;

    public ThreadA(Main main) {
        this.main = main;
    }

    public void run() {
        while (main.i <= 10) {
            synchronized (main) { // You have to use a lock object, which is main
                if (!main.istrue) {
                    try {
                        main.wait(); // operation wait()Function must be the same as lock
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("Odd:" + main.i);
                    main.i++;
                    main.istrue = false;
                    main.notifyAll();
                }
            }}}}

class ThreadB implements Runnable {
    Main main;

    public ThreadB(Main main) {
        this.main = main;
    }

    public void run() {
        while (main.i <= 10) {
            synchronized (main) { // You have to use a lock object, which is main
                if (main.istrue) {
                    try {
                        main.wait(); // operation wait()Function must be the same as lock
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("even numbers:" + main.i);
                    main.i++;
                    main.istrue = true;
                    main.notifyAll();
                }
            }}}}

//Sort out the next process
//First pass in a A and B Shared object lock main;
//When A After getting the lock, directly hand over the control of the lock and enter wait Status;
//Yes B As a result of A First got the lock, leading to B Unable to execute; until A call wait() After the release of control, B At the same time, output: even: 1, at the same time notifyAll Give Way A Ready again
//next A and B All possible cpu Time fragment, when A After getting the lock, print the odd number: 2, if B Get it again cpu Time slice, then it will enter wait Status.
//In this way, it goes back and forth, and finally it is cross printing operation.

Operation results

 

3. There are four threads, A B C D, in which D will not execute until A B C has completed its full execution, and A B C runs synchronously.

Key object: CountdownLatch object

At the beginning, we introduced thread.join(), we can let one thread wait for another thread to finish running, then we can join A B C in turn in D thread, but this also makes A B C have to execute in turn, and we want these three threads to run synchronously.
In other words, we want to achieve the following goals: three threads of A B C run at the same time, and notify d after each thread runs independently; for D, as long as A B C runs completely, d starts to run again. In this case, we can use CountdownLatch to achieve this kind of communication.

/*CountdownLatch The basic usage is:
 * 1)Create a counter, set the initial value, CountdownLatch countDownLatch = new CountDownLatch(3);
 * 2)Called in the waiting thread countDownLatch.await() method, enter the waiting state until the count value becomes 0;
 * 3)In other threads, call countDownLatch.countDown() method, which will reduce the count by 1;
 * 4)When the countDown() method of other threads changes the count value to 0, wait for the countDownLatch.await() exit now and continue with the following code.
 */

public class TestCountdownLatch {

    public static void main(String[] args) {
        runDAfterABC();
    }
    
    private static void runDAfterABC() {
        int worker = 3;
        CountDownLatch countDownLatch = new CountDownLatch(worker);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("D Wait before starting work ABC completion of jobs");
                try {
                    
                    //because worker The initial value is 3, so it is waiting until it is not equal to 0
                    countDownLatch.await();
                    System.out.println("ABC completion of jobs, D start-up");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        for (char threadName='A'; threadName <= 'C'; threadName++) {
            final String tN = String.valueOf(threadName);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(tN + "I am working.....");
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(tN + "finish the work......");
                    
                    //Every call worker Value minus one
                    countDownLatch.countDown();
                }
            }).start();
        }
    }    
}

Operation result:

 

4. Each of the three athletes is ready to run together until all three are ready

Key object: CyclicBarrier

The above CountDownLatch can be used to count down, but when the count is finished, only one thread's await() will get a response, and multiple threads cannot be triggered at the same time.

In order to realize the need of waiting for each other between threads, we can use the data structure of CyclicBarrier.

/* CyclicBarrier Basic Usage 
 * 1)First, create a public CyclicBarrier object, set the number of threads waiting at the same time, CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
 * 2)These threads start to prepare themselves at the same time. When they are ready, they need to wait for others to prepare, and then call cyclicBarrier.await(); start waiting for others;
 * 3)When the specified number of threads waiting at the same time is called cyclicBarrier.await(); means that these threads are ready, and then these threads continue to execute at the same time.
 */
public class CyclicBarrierTest {

    public static void main(String[] args) {
        runABCWhenAllReady();
    }
    
private static void runABCWhenAllReady() {
    int runner = 3;
    CyclicBarrier cyclicBarrier = new CyclicBarrier(runner);

    for (char runnerName='A'; runnerName <= 'C'; runnerName++) {
        final String rN = String.valueOf(runnerName);
        new Thread(new Runnable() {
            @Override
            public void run() { 
                try {
                    System.out.println(rN + " Ready, waiting for other threads to prepare");
                    cyclicBarrier.await(); // The current athletes are ready, waiting for others to be ready
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(rN + "Start running"); // All the athletes are ready to run together
            }
        }).start();
    }
}
}

Operation result:

 

5. After the sub thread finishes a task, the result will be returned to the main thread

Key interface: Callable

public class CallableTest {

    public static void main(String[] args) {
        doTaskWithResultInWorker();
    }
    
private static void doTaskWithResultInWorker() {
    //See Callable The biggest difference is the return paradigm V result
    Callable<Integer> callable = new Callable<Integer>() {
        
        //It needs to be rewritten here call Method, not run method
        @Override
        public Integer call() throws Exception {
            System.out.println("Task starts");
            Thread.sleep(1000);
            int result = 0;
            for (int i=0; i<=100; i++) {
                result += i;
            }
            return result;
        }
    };
    //Callable Need to put objects in FutureTask Object, in the FutureTask Object placement Thread Can start a thread
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    new Thread(futureTask).start();
    try {     
        System.out.println("Result: " + futureTask.get());
    } catch (Exception e) {
        e.printStackTrace();
    } }
}
/*Output results:
 * Task starts
 * Result: 5050
 */

Here we can learn that through FutureTask and Callable, we can get the operation results of sub threads directly in the main thread, but only need to block the main thread. Of course, if you don't want to block the main thread, you can consider using ExecutorService to put FutureTask into the thread pool to manage the execution.

 

Think too much, do too little, the gap in the middle is trouble. Think without worry, either don't think, or do more. Major [8]

Tags: Java Fragment

Posted on Wed, 10 Jun 2020 00:28:40 -0400 by Chris_Mc_1985