Java multithreading learning three (sharing and collaboration between threads)

table of Contents

           ● Introduction to yield() and join() methods

           ● Sharing between threads

           ● Collaboration between threads

Introduction to yield() and join() methods

            Yield() method: Make the current thread give up the CPU ownership ( thread give up ), but the yield time cannot be set. The lock resource will not be released , and all threads that execute yield() may be executed immediately after entering the executable state.

            join() method: add the specified thread to the current thread ( thread jumper ), you can merge two alternately executed threads into a sequential execution thread. For example, the join() method of thread A is called in thread B, and thread B will not continue to execute until thread A has finished executing.

Sharing between threads

            Java supports multiple threads to access the same object or member variables of the object at the same time. The keyword synchronized can modify the method or use it in the form of a synchronized block. It mainly ensures that multiple threads can only have one thread in the method at the same time. Or in a synchronized block, it guarantees the visibility and exclusivity of thread access to variables, also known as the built-in lock mechanism .

            Object locks and class locks : Object locks are used for object instance methods, or on an object instance. Class locks are used for static methods of a class or on the class object of a class. We know that there can be many object instances of a class, but each class has only one class object. Therefore, the object locks of different object instances do not interfere with each other, but there is only one class lock for each class.

            Note : The class lock is only a conceptual thing, not real. The class lock actually locks the corresponding class object of each class. The class lock and the object lock also do not interfere with each other.

            The use of object locks :

/**
 * @ author xiaozhou
 * @ Date 2019/4/8
 */
public class SynTest {

    private long count = 0;
    private Object obj = new Object();   //作为一个锁

    /**
     * 下面的三种写法效果是一样的
     */
    public void incCount1() {
        synchronized (obj) {
            //业务逻辑
        }
        
    }

    /**
     * 锁对象
     * 锁的是当前类的实例this
     */
    public synchronized void incCount2() {
         //业务逻辑
    }

    public void incCount3() {
        synchronized (this) {
            //业务逻辑
        }
    }

}

             The use of class locks:

    /**
     * 类锁,实际是锁类的class对象
     */
    private static synchronized void SynClass() {
        SleepTools.second(1);
        System.out.println("synClass going...");
        SleepTools.second(1);
        System.out.println("synClass end...");
    }

    private static Object obj = new Object();
    //因为obj是静态的,在全虚拟机只有一份,所以这里类似于类锁
    private void synStaticObject() {
        synchronized (obj) {   // 类似于类锁
            SleepTools.second(1);
            System.out.println("synClass going...");
            SleepTools.second(1);
            System.out.println("synClass end...");
        }
    }

            More detailed comments have been added to the code, so I won't explain it anymore.

Collaboration between threads

            Threads cooperate with each other to complete a certain job, such as: one thread modifies the value of an object, and another thread perceives the change, and then performs the corresponding operation. The whole process starts in one thread, and the final execution is another One thread. The former is the producer and the latter is the consumer. This model isolates "what" and "how" (How). The simple way is to let the consumer thread continuously loop to check whether the variable meets expectations. Unsatisfied conditions are set in the loop, and if the conditions are met, the while loop is exited, thus completing the consumer's work. (About the understanding of producers and consumers, I originally planned to write an article by myself, but after I found an article that allows you to thoroughly understand the producer-consumer problem , I think I don’t have a thorough understanding of others, even if I write The article is not well written by others, so I recommend it to everyone here, and I have learned it myself.) But I think there are two problems with the producer-consumer model:

           (1) It is difficult to ensure timeliness

           (2) It is difficult to reduce expenses. If you reduce the sleep time, such as sleep for 1 millisecond, so that consumers can more quickly find changes in conditions, but may consume more processor resources, resulting in unwarranted waste.

Wait/notify mechanism (wait/notify/notifyAll)

           Waiting/notification means that a thread A calls the wait() method of the object O to enter the waiting state, while another thread B calls the notify() or notifyAll() method of the object O, and the thread A receives the notification from the object O. The wait() method returns, and subsequent operations are performed. The above two threads complete the interaction through the object O, and the relationship between wait() and notify()/notifyAll() on the object is like a switch signal to complete the interactive work between the waiting party and the notifying party.

          1.  wait()

              The thread that calls this method enters the waiting state. It will only return when it waits for another thread’s notification or is interrupted. Before calling this method, the thread must obtain the object monitor lock of the object, so it can only be used in a synchronized method or synchronized code block. Call the wait () method. After calling the wait() method, the current thread will release the lock of the object.

              wait(long timeout)

               Timeout waits for a period of time, the parameter is milliseconds, that is, after waiting for the milliseconds of the incoming parameter, if it does not wait for notification or interruption, it will timeout and return.

              wait(long timeout, int nanos)

               For more precise control of the timeout period, the second parameter is in the order of nanoseconds.

          2. notify()

              Arbitrarily select one of the threads waiting on the object for notification , so that the thread calling the wait() method switches from the waiting state to the ready state, waiting for a chance to acquire the lock again, so that the thread calling the wait() method can Exit at the wait() method. After calling notify(), the current thread will not release the object lock immediately, but will not release the lock until the program finishes executing the synchronized block. The thread that has not acquired the lock enters the waiting state again.

          3. notifyAll()

              Make all threads waiting on the object exit from the waiting state and enter the runnable state.

 

         Below we use a case to demonstrate the use of wait/notify:

          Case: Two threads, one thread prints the value of 1-52, and the other thread prints the value of AZ.

                     The print format is: 12A 34B 56C 78D........

          First, let's analyze it. According to the requirements of the case, one of the two threads prints numbers and the other prints letters AZ. Okay, now we will implement it step by step:

          1. First print the thread implementation of the number:

public class Number implements Runnable {

        private Object object;

        public Number(Object obj) {
            this.object = obj;
        }

        @Override
        public void run() {
            synchronized (object) {
                for (int i = 1; i < 53; i++) {
                    //代码块1
                    if (i >= 1 && i % 2 == 1) {
                        System.out.print(" "+i);
                    }
                    //代码块2
                    if (i % 2 == 0) {
                        System.out.print(i);
                        object.notifyAll();
                        try {
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

              I think what needs to be explained in this code is the two code blocks in the comments. In the following explanations, we all take the printed 12A as an example.

              Code block 1 is the logical judgment of output 1, including 3, 5, etc. printed later can be qualified by this condition.

              Code block 2 is to make a logical judgment of output 2, here is the output of a number that can be divisible by 2. After the output, we need to notify another thread to print the letter A, so this thread will call the wait() method to wait for another thread to finish the operation of outputting the letter A, and then issue a notification before continuing to print the numbers as required.

             2. Now let's look at the implementation of another thread that prints letters.

public class CharacterThread implements Runnable {

    Object object;

    public CharacterThread(Object obj) {
        this.object = obj;
    }

    @Override
    public void run() {
        synchronized (object) {
            for (char i = 'A'; i <= 'Z'; i++) {
                System.out.print(i);
                object.notifyAll();
                try {
                    if (i < 'Z'){
                        object.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

            In the for loop, each letter is printed first, and after the printing is completed, the thread that prints the number needs to be notified to make the next number. So after printing a letter, the current thread cannot continue to print letters for the time being, and it needs to wait until the thread that prints numbers sends a notification to continue printing. Of course these logics are based on i<'Z'.

            3. Then call it in the Main() method:

public class PrintInfoDemo3 {
    public static void main(String[] args) {
        Object obj = new Object();
        new Thread(new NumberThread(obj)).start();
        new Thread(new CharacterThread(obj)).start();
    }
}

            operation result:

   Prove that wait() method releases the lock and notify() does not release the lock

   1. The wait() method releases the lock:

public class WaitThread implements Runnable {

        private Object obj;

        public WaitThread(Object object) {
            this.obj = object;
        }

        @Override
        public void run() {
            synchronized (obj) {
                try {
                    System.out.println(Thread.currentThread().getName() + "Begin wait()");
                    obj.wait();
                    System.out.println(Thread.currentThread().getName() + "End wait()");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

       Called in the main() method

 public static void main(String[] args) {
        Object object = new Object();
        WaitThread waitThread1 = new WaitThread(object);
        WaitThread waitThread2 = new WaitThread(object);
        new Thread(waitThread1).start();
        new Thread(waitThread2).start();
    }

The result of the operation is:

If the wait() method does not release the lock, then Thread-0 will not enter the synchronous code block to execute the print statement. Therefore, it is proved that the wait() method will release the lock.

2. Proof that the notify() method does not release the lock

    Create a new thread to rewrite the logic in the run() method

public class NotifyThread1 implements Runnable {

        private Object obj;

        public NotifyThread1(Object object) {
            this.obj = object;
        }

        @Override
        public void run() {
            synchronized (obj) {
                try {
                    System.out.println("Begin wait(),ThreadName = " + Thread.currentThread().getName());
                    obj.wait();
                    System.out.println("End wait(), ThreadName = " + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

      Create a new thread to rewrite the logic of the run() method

 public class NotifyThread2 implements Runnable {

        private Object obj;

        public NotifyThread2(Object object) {
            this.obj = object;
        }

        @Override
        public void run() {
            try {
                synchronized (obj) {
                    System.out.println("Begin notify(), ThreadName = " + Thread.currentThread().getName());
                    obj.notify();
                    Thread.sleep(5000);
                    System.out.println("End notify(), ThreadName = " + Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

            Then call in the main() method

 public static void main(String[] args) {
        Object object = new Object();
        NotifyThread1 notifyThread1 = new NotifyThread1(object);
        NotifyThread2 notifyThread2 = new NotifyThread2(object);
        new Thread(notifyThread1).start();
        new Thread(notifyThread2).start();
    }

          Let's take a look at the results of the operation:

          According to the running results, it can be analyzed: if the notify() method will release the lock, then after Thread-1 calls notify(), other threads must intervene in the synchronization code block during Thread.sleep(5000), and the running result is clearly waiting for Thread -1 The execution continues after the sleep is completed, so it proves that the notify() method cannot release the lock.

 

This article is now complete, and it mainly introduces the sharing and collaboration between threads. In the next article, I will continue to learn about multithreading.

Related article reading

java multi-threaded learning (1)

java multithreading learning two (thread start and termination)

Java multithreading learning three (sharing and collaboration between threads)

 

 

 

 

Guess you like

Origin blog.csdn.net/zhourui_1021/article/details/89350823