Chapter 5 Thread Interruption and Waiting for Wake-up Mechanism

thread interruption mechanism

Interview questions

void interrupt() interrupt this thread
static boolean interrupted() Test whether the current thread has been interrupted
boolean isInterrupted() Test whether this thread has been interrupted
  • Have you ever understood the three methods? Where to use it?
  • How to stop a running thread?
  • How to interrupt a running thread? ?

What is the interrupt mechanism?

first

  • A thread should not be forcibly interrupted or stopped by other threads, but should be stopped by the thread itself. Therefore, Thread.stop, Thread.suspend, and Thread.resume have all been deprecated.
method name static Function Description
stop() Stop thread running
suspend() Suspend (pause) a thread from running
resume() Resume thread running

Secondly

  • There is no way to stop a thread immediately in Java, but stopping a thread is particularly important, such as canceling a time-consuming operation.
  • Therefore, Java provides a negotiation mechanism for stopping threads - interrupts.
    • Interrupts are just a collaborative negotiation mechanism. Java does not add any syntax to interrupts. The interrupt process completely requires programmers to implement it themselves.

**Definition: **Interrupt a running thread (the run method has not yet been executed). Ordinary threads will automatically stop after the run method is executed. Our interrupt is actually to change the status of the thread. If we want the thread to terminate, only After the run method is executed, it will terminate naturally.

If we want to interrupt a thread, you need to manually call the interrupt method of the thread. This method is only to set the interrupt flag of the thread to true. There is no real stop of the thread here, and then you need to write your own code continuously Detect the flag of the current thread. If it is true, it means that other threads require this thread to be interrupted. What to do at this time requires you to write code to achieve it.

  • There is a flag in each thread object to indicate whether the thread is interrupted; if the flag is true, it means it is interrupted, if it is false, it means it is not interrupted;

  • Set the flag bit of the thread to true by calling the interrupt method of the thread object; it can be called in other threads or in its own thread.

  • eg. The customer smokes in a non-smoking restaurant, and the waiter hopes that he will stop smoking. Instead of forcibly stopping him from smoking, he marks his flag as true. The specific cessation of smoking still requires the customer to stop himself. (Reflecting the consultation mechanism)

Realize three interrupt methods

  • Interrupt via shared variable

  • Via AtomicBoolean (atomic Boolean)

  • Use the Thread.interrupt() static method to interrupt

Implemented through a volatile variable

  • Volatile ensures visibility. After t2 modifies the flag bit, it can be seen by t1 immediately.
public class InterruptDemo {
    
    
    static volatile boolean isStop = false;

    public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(()->{
    
    
            while (true){
    
    
                if (isStop){
    
    
                    System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止");
                    break;
                }
                System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
            }
        },"t1").start();
        Thread.sleep(20);
        new Thread(()->{
    
    
            isStop = true;
        },"t2").start();
    }
}
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1	 isStop被修改为true,程序终止

via AtomicBoolean (atomic boolean)

public class InterruptDemo {
    
    
    static volatile boolean isStop = false;
    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
    public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(()->{
    
    
            while (true){
    
    
                if (atomicBoolean.get()){
    
    
                    System.out.println(Thread.currentThread().getName()+"\t atomicBoolean被修改为true,程序终止");
                    break;
                }
                System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
            }
        },"t1").start();
        Thread.sleep(20);
        new Thread(()->{
    
    
           atomicBoolean.set(true);
        },"t2").start();
    }
}
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1 ------hello volatile
//t1   atomicBoolean被修改为true,程序终止

Implemented through the interrupt api method that comes with the Thread class

public class InterruptDemo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Thread(() -> {
    
    
            while (true) {
    
    
                if (Thread.currentThread().isInterrupted()) {
    
    
                    System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为true,程序终止");
                    break;
                }
                System.out.println("t1 ------hello volatile");//----------------------如果没停止,那就一直打印
            }
        }, "t1");
        t1.start();
        Thread.sleep(20);
        new Thread(()->{
    
    
            t1.interrupt();
        },"t2").start();
    }
}
  • public void interrupt() instance method, the instance method interrupt() just sets the interrupt status of the thread to true, initiates a negotiation without stopping the thread immediately
  • public static boolean interrupted() static method, Thread.interrupted(); determine whether the thread is interrupted, and clear the current interrupt status. This method does two things:
    • 1 Returns the interrupt status of the current thread
    • 2 Set the interrupt status of the current thread to false (this method is a bit difficult to understand, because the results of two consecutive calls may be different.)
  • public boolean isInterrupted() instance method to determine whether the current thread is interrupted (by checking the interrupt flag bit)

API source code analysis

Instance method interrupt(), no return value

//Thread.java
public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag----调用了interrupt0()方法
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

//Thread.java
    /* Some private helper methods */
    private native void setPriority0(int newPriority);
    private native void stop0(Object o);
    private native void suspend0();
    private native void resume0();
    private native void interrupt0();  //---------------------------调用了c底层
    private native void setNativeName(String name);

Instance method isInterrupted, returns a Boolean value

//Thread.java
public boolean isInterrupted() {
    return isInterrupted(false);
}
//Thread.java
private native boolean isInterrupted(boolean ClearInterrupted);//也调用了c底层

Specifically, when interrupt() is called on a thread:

  • If the thread is normally active, then the thread's interrupt flag is set to true, and that's it. Threads whose interrupt flags are set will continue to run normally without being affected. Therefore, interrupt() cannot really interrupt the thread, and the called thread needs to cooperate on its own.

  • When a thread calls sleep/wait/join and other methods and is blocked, thread.interrupt() is received. 就会抛出一个中断异常,InterruptedExceptionWhen this exception is thrown (no matter which judgment method is used),当前线程的中断状态会被清除(也就是Thread类中那个特别设置的中断属性)

  • (Interrupting inactive threads will have no effect, see the case below)

If the interrupt flag of the current thread is true, will the thread stop immediately?

  • no
    • It only sets an interrupt status and does not actually stop the thread.
  • See if the interrupt will immediately stop this 300 thread
    • No, although the interrupt flag has changed. But i keeps looping
public class InterruptDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 300; i++) {
                System.out.println("---------" + i);
            }
            System.out.println("after t1.interrupt()---第2次----"+Thread.currentThread().isInterrupted());
        }, "t1");
        t1.start();
        System.out.println("before t1.interrupt()----"+t1.isInterrupted());
        t1.interrupt();
        Thread.sleep(3);
        System.out.println("after t1.interrupt()---第1次---"+t1.isInterrupted());
        Thread.sleep(3000);
        t1.interrupt();
        System.out.println("after t1.interrupt()---第3次---"+t1.isInterrupted());
    }
}
//before t1.interrupt()----false
//---------0
//---------1
//---------2
//---------3
//....
//---------136
//after t1.interrupt()---第1次---true    ------此处中断标志位设置为了true,但是t1仍然在运行
//---------137
//---------298
//---------299
//after t1.interrupt()---第2次----true
//after t1.interrupt()---第3次---false//中断不活动的线程不会产生任何影响,线程结束后应该是自动变为了false
  • before t1.interrupt()----false
    • The default interrupt flag bit of a thread is false
  • after t1.interrupt()—primary—true
    • The interrupt flag bit of our thread is true, but the thread has not ended and is still printing
  • after t1.interrupt()—secondary----true
    • Proving once again that although the interrupt flag bit becomes true, the thread does not end
  • after t1.interrupt()—Third—false
    • This output shows that the t1 thread has ended. Although we call interrupt() again, our interrupt flag is still false.
    • Explain that interrupting an inactive thread has no effect

Behind-the-scenes case - in-depth

  • On the skeleton of our basic interrupt program + a sleep blocking
  • Interrupt exceptions will cause the program to loop infinitely.
public class InterruptDem02 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
                while (true){
                    if (Thread.currentThread().isInterrupted()){
                        System.out.println(Thread.currentThread().getName()+"\t"+
                                "中断标志位:"+Thread.currentThread().isInterrupted()+"程序终止");
                        break;
                    }
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                         // Thread.currentThread().interrupt();  假如加了这个,程序可以终止,只会爆异常
                        e.printStackTrace();
                    }
                    System.out.println("-----hello InterruptDemo03");
                }
        }, "t1");
        t1.start();
        TimeUnit.MILLISECONDS.sleep(1);
        new Thread(()-> t1.interrupt(),"t2").start();
    }
}
/*java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.lsc.day04.InterruptDem02.lambda$main$0(InterruptDem02.java:23)
	at java.lang.Thread.run(Thread.java:748)
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
-----hello InterruptDemo03
*/
  • When a thread in a blocked state (such as sleep, wait, join, etc.) receives an interrupt negotiation (calling the method of the current thread object in another thread) interrupt, the thread will immediately exit the blocked state and set the interrupt flag bit. Clearing will cause an exception (InterruptedException) to occur directly, but the thread will not end.
  • 1 The interrupt flag is false by default
  • 2 t2 ----->t1 issued an interrupt negotiation, t2 called t1.interrupt(), and the interrupt flag bit was true
  • 3 The interrupt flag bit is true. Under normal circumstances, the program stops, -
  • 4 If the interrupt flag bit is true, under abnormal circumstances, InterruptedException will clear the interrupt status and receive InterruptedException. A false interrupt flag causes an infinite loop.
  • 5 In the catch block, the interrupt flag bit needs to be set to true again, and the call will stop after 2 calls.

Static method Thread.interrupted(), talk about your understanding

public static boolean interrupted()
static method, Thread.interrupted(); determines whether the thread has been interrupted and clears the current interruption status. This method does two things:

  • 1 Returns the interrupt status of the current thread
  • 2 Set the interrupt status of the current thread to false (this method is a bit difficult to understand, because the results of two consecutive calls may be different.)
public class InterruptDemo3 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
        System.out.println("-----1");
        Thread.currentThread().interrupt();//中断标志位设置为true
        System.out.println("-----2");
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
        System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
    }
}
main	false
main	false
-----1
-----2
main	true
main	false

Look at the source code, interrupted() vs. isInterrupted()

  • public static boolean interrupted() {
         return currentThread().isInterrupted(true);
    }
        
    private native boolean isInterrupted(boolean ClearInterrupted);
    
    
  • public boolean isInterrupted() {
          
          
         return isInterrupted(false);
    }
    
    private native boolean isInterrupted(boolean ClearInterrupted);
    
    • They all call the native method isInterrupted at the bottom level.

    • It's just that one of the parameters passed in to ClearInterrupted is true, and the other is false.

  • True in the static method interrupted() means clearing the current interrupt status.

  • The instance method isInterrupted does not.

What is LockSupport

  • Official explanation: Basic thread used to create locks and other synchronization classes 阻塞原语.

The core is the park() and unpark() methods

  • The park() method blocks the thread

  • The unpark() method is to unblock the thread

Thread wait wake-up mechanism

When we develop multi-threads, sometimes some threads are executed first. After these threads end, other threads continue to execute. For example, in our life when playing basketball, everyone on the court is a thread. Which player must Pass the ball to another player first, then the other player can shoot. This means that after one action of one thread is executed, the action of another thread can be executed.

  • Use the method in Object to make the thread wait, and use the method wait()in Object to wake up the threadnotify()

  • Use the Condition wait()method in the JUC package to make the thread wait, and use signal()the method to wake up the thread

  • The LockSupport class can block the current thread and wake up the specified blocked thread.

Object's wait and notify

Wait method

public final void wait() throws InterruptedException {
    
    
   wait(0);
}
public final void wait(long timeout, int nanos) throws InterruptedException {
    
    
   if (timeout < 0) {
    
    
      throw new IllegalArgumentException("timeout value is negative");
   }
   if (nanos < 0 || nanos > 999999) {
    
    
      throw new IllegalArgumentException("nanosecond timeout value out of range");
   }
	if (nanos > 0) {
    
    
      timeout++;
    }
	wait(timeout);
}
  • The crazy method, dead waiting, the thread enters the blocking state (WAITING) until other threads call the notify method to wake up

  • Wait for a period of time. If the thread is awakened within this time, execution will continue. If no other thread wakes up the thread after the corresponding time, the thread will no longer wait and resume execution of the waiting method.

What the await method does

  • The prerequisite for calling the wait method is to obtain the lock of this object (synchronized object lock, if multiple threads compete for this lock, only one thread obtains the lock, and other threads will be in the blocking queue)

  • Make the thread currently executing the code wait. (Put the thread in the waiting queue)

  • 调用wait方法会释放锁

  • If certain conditions are met, it will retry to obtain the lock. After being awakened, execution will not resume immediately, but will enter the blocking queue and compete for the lock.

Three ways to end the wait

  • Other threads call the object's notify method.
  • wait wait timeout (wait method provides a version with timeout parameter to specify the waiting time).
  • Other threads call the interrupted method of the waiting thread, causing wait to throw an InterruptedException exception.

notify

public final native void notify();
public final native void notifyAll();
  • notify() randomly wakes up a thread in a waiting state
  • notifyAll() wakes up all threads in the waiting state
  • The method notify() must also be called in a synchronized method or synchronized block. This method is used to notify other threads that may be waiting for the object lock of the object, notify them, and enable them to reacquire the object lock of the object.
  • If there are multiple threads waiting, the thread scheduler randomly selects a thread in the wait state. (There is no "first come, first served") After the notify() method, the current thread will not release the object lock immediately. The object lock will not be released until the thread executing the notify() method finishes executing the program, that is, after exiting the synchronized code block. .
public class ObjectWaitAndNotify {
    
    
    public static void main(String[] args) {
    
    
        Object objectLock = new Object();
        new Thread(()->{
    
    
            synchronized (objectLock){
    
    
                System.out.println(Thread.currentThread().getName()+"\t ---- come in");
                try {
    
    
                    objectLock.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
        },"t1").start();
        //暂停几秒钟线程
        try {
    
     TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
        new Thread(()->{
    
    
            synchronized (objectLock){
    
    
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
            }
        },"t2").start();
    }
}

异常1—去掉synchronized

  • Note that to use wait and notify you must add synchronized
public class ObjectWaitAndNotify {
    
    
    public static void main(String[] args) {
    
    
        Object objectLock = new Object();
        new Thread(()->{
    
    
//            synchronized (objectLock){
    
    
                System.out.println(Thread.currentThread().getName()+"\t ---- come in");
                try {
    
    
                    objectLock.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
//            }
            System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
        },"t1").start();
        //暂停几秒钟线程
        try {
    
     TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
        new Thread(()->{
    
    
//            synchronized (objectLock){
    
    
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
//            }
        },"t2").start();
    }
}
t1	 ---- come in
Exception in thread "t1" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.lsc.day04.ObjectWaitAndNotify.lambda$main$0(ObjectWaitAndNotify.java:20)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "t2" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.lsc.day04.ObjectWaitAndNotify.lambda$main$1(ObjectWaitAndNotify.java:31)
	at java.lang.Thread.run(Thread.java:748)

异常2—把notify和wait的执行顺序对换

  • The order of instructions cannot be reversed
public class ObjectWaitAndNotify{
    
    
    public static void main(String[] args){
    
    
        Object objectLock = new Object();
        new Thread(() -> {
    
    
            try {
    
     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
            synchronized (objectLock) {
    
    
                System.out.println(Thread.currentThread().getName()+"\t ---- come in");
                try {
    
    
                    objectLock.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒了");
        },"t1").start();
        new Thread(() -> {
    
    
            synchronized (objectLock) {
    
    
                objectLock.notify();//这个先执行了
                System.out.println(Thread.currentThread().getName()+"\t ---发出通知");
            }
        },"t2").start();
    }
}
//一直处于循环中

Summary

  • The wait and notify methods must be in a synchronized block or method and used in pairs.
  • Wait first and then notify is OK, order

背后工作原理解析

img

  • It must be clear who the lock resource is. What causes competition must be that the lock object called by the thread must be the same. If the competition is not for the same lock, then it will not enter the same blocking queue.
  • Only when the wake-up thread completes execution and the blocking queue thread can a thread obtain the lock
    • How to understand blocking queue and waiting queue? For example, if the current t1 thread obtains the lock resource, then if t2 and t3 want to compete for this lock, t2 and t3 must be in the blocking queue. When the t1 thread obtains the lock and calls the wait method, it enters the waiting state. Enter the waiting queue, and then release the lock resource, then t2 and t3 will compete for the lock resource, and then one of them will be obtained, and so on. When the three threads are in the waiting queue, when the notify thread is called, one of the threads in the waiting queue will enter. Blocking queue, but even if there is only one thread in the blocking queue, the lock will not be obtained immediately, because the thread executing the notify thread will also occupy the lock, and the lock must be released after the notify thread ends.
    • If notifyAll is called, all three threads will be put into the blocking queue and then compete to lock resources.

wait和sleep的区别

  • In fact, in theory, wait and sleep are completely incomparable, because one is used for communication between threads, and the other is to block the thread for a period of time. The only similarity is that both can make the thread give up execution for a period of time.
  • If there are commonalities, introduce them first. If not, just introduce them separately.
  • The wait method is a method provided by the Object class and needs to be used with a synchronized lock. Calling the wait method will release the lock. The waiting thread will be awakened by other threads or automatically awakened after timeout. After awakening, it needs to compete for the synchronized lock again to continue execution.
  • sleep is a method provided by the Thread class (it does not have to be used with synchronized). Call the sleep method to enter the TIMED_WAITING state. If the lock is occupied, the lock will not be released. It will wake up automatically when the time is up.
  • Because the target of Java lock is an object, and wait needs to release the lock, the targets are all objects, so it is defined in the Object class, and Sleep() does not need to release the lock, so its target is a thread, so it is defined In Thread class

为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里

  • The target of locks in Java is objects. All Java classes inherit Object. Java wants any object to be used as a lock, and wait(), notify() and other methods are used to wait for the object's lock or wake up the thread. In Java There is no lock available for any object in the thread, so the calling method of any object must be defined in the Object class.

为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用

  • When a thread needs to call the wait() method of an object, the thread must own the lock of the object . Then it will release the object lock and enter the waiting state (waiting queue) until other threads call notify() on the object. method.
  • Similarly, when a thread needs to call the object's notify() method, it will release the object's lock (after executing the lock code content) so that other waiting threads can obtain the object lock.
  • Since all of these methods require the thread to hold the object's lock, which can only be achieved through synchronization, they can only be called in synchronized methods or synchronized blocks.

await and signal in Condition interface

public class ConditionAwaitAndSignal {
    
    
    public static void main(String[] args) {
    
    
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
    
    
            lock.lock();
            try {
    
    
                System.out.println(Thread.currentThread().getName()+"\t-----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName()+"\t -----被唤醒");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                lock.unlock();
            }
        },"t1").start();
        //暂停几秒钟线程
        try {
    
     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
        new Thread(()->{
    
    
            lock.lock();
            try {
    
    
                condition.signal();
            }catch (Exception e){
    
    
                e.printStackTrace();
            }
            finally {
    
    
                lock.unlock();
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"我要进行唤醒");
        },"t2").start();
    }
}

The principle of exception 1 is the same as above

  • Still returns IllegalMonitorStateException

Abnormal 2 The principle is the same as above

  • still looping

Summary

  • await and notify are similar to wait and notify above

  • The thread waiting and waking up methods in Condition need to acquire the lock first

  • You must first await and then signal, not the other way around.

Restrictions on the use of Object and Condition

Summarize

  • The thread must first obtain and hold the lock, and must be in the lock block (synchronized or lock)

  • You must wait first and then wake up before the thread can be awakened.

Park waiting and unpark wake-up in LockSupport class

what is

  • Block and wake up threads through the park() and unpark(thread) methods

Official website explanation

  • LockSupport is the basic thread used to create locks and other synchronization classes 阻塞原语.

  • The LockSupport class uses a concept called Permit to achieve the function of blocking and waking up threads. Each thread has a permission (permit).

  • permit has only two values ​​1 and 0.

    • The default is 0.
    • 0 is blocking
    • 1 is wake up
  • Permission can be regarded as a (0,1) semaphore (Semaphore), but unlike Semaphore, the cumulative upper limit of permission is 1.

block

  • park()/park(Object blocker)

When calling LockSupport.park(), it was found that it called the unsafe class and passed a 0 by default.

public static void park() {
    
    
     UNSAFE.park(false, 0L);
}
public native void park(boolean var1, long var2);
  • Permit defaults to zero, so as soon as the park() method is called, the current thread will block until another thread sets the permit of the current thread to 1, the park method will be awakened, and then the permit will be set to zero again and return .

wake

When calling LockSupport.unpark();, the unsafe class is also called

public static void unpark(Thread thread) {
    
    
        if (thread != null)
            UNSAFE.unpark(thread);
}
public native void unpark(Object var1);
  • After calling the unpark(thread) method, the permission of the thread thread will be set to 1
    • Note that calling the unpark method multiple times will not accumulate, and the permit value is still 1.
  • The thread thread will be automatically awakened, that is, the previously blocked LockSupport.park() method will return immediately.
public class LockSupportParkAndUnPark {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> {
    
    
            System.out.println(Thread.currentThread().getName()+"\t----------come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了");
        }, "t1");
        t1.start();
        new Thread(() -> {
    
    
            System.out.println(Thread.currentThread().getName()+"\t----------come in");
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"\t----------我来唤醒了");
        }, "t2").start();
    }
}
t1	----------come in
t2	----------come in
t2	----------我来唤醒了
t1	----------被唤醒了

The previous mistake was to wake up first and then wait. LockSupport still supports it.

public class LockSupportParkAndUnPark {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> {
    
    
            System.out.println(Thread.currentThread().getName()+"\t----------come in");
            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            LockSupport.park();
            System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了");
        }, "t1");
        t1.start();
        new Thread(() -> {
    
    
            System.out.println(Thread.currentThread().getName()+"\t----------come in");
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"\t----------我来唤醒了");
        }, "t2").start();
    }
}
t1	----------come in
t2	----------come in
t2	----------我来唤醒了
t1	----------被唤醒了

The sleep method wakes up after 3 seconds, and executing park is invalid and has no blocking effect. The explanation is as follows. Executing unpark(t1) first causes the above park method to be ineffective and the time is the same.

  • Similar to the ETC on expressways, you buy the pass in advance to unpark, and when you go to the turnstile, you can directly lift the railing to let the pass pass, and there is no park to intercept it.
public class LockSupportDemo
{
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(()->{
    
    
            try {
    
    TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {
    
    e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t----------come in"+"\t"+System.currentTimeMillis());
            LockSupport.park();
            LockSupport.park();
            System.out.println(Thread.currentThread().getName()+"\t----------被唤醒了"+"\t"+System.currentTimeMillis());
        },"t1");
        t1.start();

        new Thread(()->{
    
    
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"\t-----发出通知,去唤醒t1");
        },"t2").start();
    }
}
//t2  -----发出通知,去唤醒t1
//t1  ----------come in  1654750970677--------------------卡在这里了

  • A thread's pass can only have one

Summary

  • Lock Support is the basic thread blocking primitive used to create locks and other synchronization classes.

  • Lock Support is a thread blocking tool class. All methods are static methods, which can allow the thread to block at any location. There are also corresponding wake-up methods after blocking. Ultimately, Lock Support calls native code in Unsafe.

  • Lock Support provides the park() and unpark() methods to implement the process of blocking threads and unblocking threads.

  • Lock Support has a permit associated with each thread that uses it.

  • Each thread has an associated permit, and there is only one permit at most. Repeated calls to unpark will not accumulate credentials.

Interview question
: Why can the original calling sequence of wait/notify be broken?

  • Because unpark obtains a certificate, and then calls the park method, the certificate can be consumed legitimately, so it will not block.
  • Once the voucher is issued first, everything can go smoothly afterwards.

Why does it block twice after waking up twice, but the final result still blocks the thread?

  • Because the number of vouchers is at most 1, calling unpark twice in a row has the same effect as calling unpark once, and only one voucher will be added; while calling park twice needs to consume two vouchers, which cannot be released because the vouchers are not enough.

Correct usage posture of wait and notify

The question is: Xiao Nan must have a cigarette to work, but others do not need cigarettes to work.

sleep解决

public class ThreadDemo6 {
    
    
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                System.out.println("有烟吗" + hasCigarette);
                if (!hasCigarette) {
    
    
                    System.out.println("没烟,先歇会!");
                    try {
    
    
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println("有烟没?" + hasCigarette);
                if (hasCigarette) {
    
    
                    System.out.println("可以开始干活了");
                }
            }
        }, "小南").start();
        for (int i = 0; i < 5; i++) {
    
    
            new Thread(() -> {
    
    
                synchronized (room) {
    
    
                    System.out.println("其他人可以干活了");
                }
            }, "其它人").start();
        }
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
    
    
            // 这里能不能加 synchronized (room)?
            hasCigarette = true;
            System.out.println("烟到了");
        }, "送烟的").start();
    }
}
有烟吗false
没烟,先歇会!
烟到了
有烟没?true
可以开始干活了
其他人可以干活了
其他人可以干活了
其他人可以干活了
其他人可以干活了
其他人可以干活了
  • Other working threads have to be blocked all the time, which is too inefficient.
  • Xiaonan Thread must sleep for 2 seconds before waking up. Even if the cigarette is delivered in advance, he cannot wake up immediately.
  • The cigarette delivery thread cannot be locked. After adding synchronized (room), it’s like Xiaonan locked the door inside to sleep, and the cigarettes can’t be delivered to the door at all. Without synchronized main, it’s like the main thread came in through the window.
  • Solution, use wait-notify mechanism

wait/notify解决

  public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                System.out.println("有烟吗" + hasCigarette);
                if (!hasCigarette) {
    
    
                    System.out.println("没烟,先歇会!");
                    try {
    
    
                        room.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println("有烟没?" + hasCigarette);
                if (hasCigarette) {
    
    
                    System.out.println("可以开始干活了");
                }
            }
        }, "小南").start();
        for (int i = 0; i < 5; i++) {
    
    
            new Thread(() -> {
    
    
                synchronized (room) {
    
    
                    System.out.println("可以开始干活了");
                }
            }, "其它人").start();
        }
       TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                hasCigarette = true;
                System.out.println("烟到了");
                room.notify();
            }
        }, "送烟的").start();
    }
有烟吗false
没烟,先歇会!
可以开始干活了
可以开始干活了
可以开始干活了
可以开始干活了
可以开始干活了
烟到了
有烟没?true
可以开始干活了
  • Solved the problem of blocking other threads doing work
    • Because entering the wait queue will release the lock
  • But what if there are other threads waiting for the condition?

当不止一个线程在wait队列中

Add a little girl, she has to wait until the takeaway arrives before she can work

public class ThreadDemo6 {
    
    
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                System.out.println("小南:有烟吗" + hasCigarette);
                if (!hasCigarette) {
    
    
                    System.out.println("小南:没烟,先歇会!");
                    try {
    
    
                        room.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println("小南:有烟没?" + hasCigarette);
                if (hasCigarette) {
    
    
                    System.out.println("小南:可以开始干活了");
                }
            }
        }, "小南").start();
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                Thread thread = Thread.currentThread();
                System.out.println("小女:外卖送到了吗"+hasCigarette);
                if (!hasTakeout) {
    
    
                    System.out.println("小女:没外卖,先歇会!");
                    try {
    
    
                        room.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println("小女:外卖送到了吗"+hasCigarette);
                if (hasTakeout) {
    
    
                    System.out.println("小女:可以开始干活了");
                } else {
    
    
                    System.out.println("小女:没干成活...");
                }
            }
        }, "小女").start();
        for (int i = 0; i < 5; i++) {
    
    
            new Thread(() -> {
    
    
                synchronized (room) {
    
    
                    System.out.println("可以开始干活了");
                }
            }, "其它人").start();
        }
       TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
    
    
            synchronized (room) {
    
    
                hasTakeout = true;
                System.out.println("外卖到了噢!");
                room.notify();
                //room.notifyAll();
            }
        }, "送外卖的").start();
    }

}
小南:有烟吗false
小南:没烟,先歇会!
小女:外卖送到了吗false
小女:没外卖,先歇会!
可以开始干活了
可以开始干活了
可以开始干活了
可以开始干活了
可以开始干活了
外卖到了噢!
小南:有烟没?false
  • We found that our takeout was actually delivered, but the wake-up was indeed the Xiaonan thread waiting for cigarettes, causing the program to get stuck, and the Xiaonan thread kept waiting.
  • notify can only randomly wake up a thread in a WaitSet. If there are other threads waiting at this time, the correct thread may not be woken up, which is called [false wakeup]
  • Solution, change to notifyAll

优化if

if (!hasCigarette) {
    
    
	log.debug("没烟,先歇会!");
	try {
    
    
		room.wait();
	} catch (InterruptedException e) {
    
    
		e.printStackTrace();
	}
}
  • Although notify will wake up the required threads, there is still a problem. We should not have woken up Xiao Nan, but we still woke him up, which also caused Xiao Nan to fail to work in the end.
while (!hasCigarette) {
    
    
	log.debug("没烟,先歇会!");
	try {
    
    
		room.wait();
	} catch (InterruptedException e) {
    
    
		e.printStackTrace();
	}
}
  • If we change if to while, even if we wake up the thread that should not be woken up, we can still let it enter the waiting queue again.

Summarize

  • When using wait/notify, notifyAll should be used to prevent the thread that should be awakened from not being awakened.
  • When used with wait/notify, while should be used instead of if. In order to prevent being awakened when it should not be awakened, it can enter the waiting queue again.

practise

fixed run order

For example, 2 must be printed after 1

public class Demo1 {
    
    
    //用来同步的对象
    static Object obj = new Object();
    //t2 运行标记 ,代表t2是否执行过
    static boolean t2runed = false;
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> {
    
    
            
            synchronized (obj) {
    
    
                //如果t2没有执行过
                while (!t2runed) {
    
    
                    try {
    
    
                        obj.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                //说明t2执行过
                System.out.println(1);
            }

        });
        Thread t2 = new Thread(() -> {
    
    
            System.out.println(2);
            synchronized (obj) {
    
    
                t2runed = true;
                // 通知 obj 上等待的线程(可能有多个,因此需要用 notifyAll)
                obj.notifyAll();
            }
        });
        t1.start();
        t2.start();
    }
}
public class Demo1 {
    
    
    //用来同步的对象
    static Object obj = new Object();
    //t2 运行标记 ,代表t2是否执行过
    static boolean t2runed = false;
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> {
    
    
            try {
    
     Thread.sleep(1000); } catch (InterruptedException e) {
    
     }
            // 当没有『许可』时,当前线程暂停运行;有『许可』时,用掉这个『许可』,当前线程恢复运行
            LockSupport.park();
            System.out.println("1");
        });
        Thread t2 = new Thread(() -> {
    
    
            System.out.println("2");
            // 给线程 t1 发放『许可』(多次连续调用 unpark 只会发放一个『许可』)
            LockSupport.unpark(t1);
        });
        t1.start();
        t2.start();
    }
}

alternate output

Thread 1 outputs a 5 times, thread 2 outputs b 5 times, and thread 3 outputs c 5 times. Now it is required to output abcabcabcabcabc how to achieve

wait/notify version

class SyncWaitNotify {
    
    
    private int flag;
    private int loopNum;

    public SyncWaitNotify(int flag, int loopNum) {
    
    
        this.flag = flag;
        this.loopNum = loopNum;
    }
    public void print(int waitFlag, int nextFlag, String str){
    
    
        for (int i = 0; i < loopNum; i++) {
    
    
            synchronized (this){
    
    
                //没有轮到这个线程打印
                while (flag!=waitFlag){
    
    
                    try {
    
    
                        this.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println(str);
                flag=nextFlag;
                this.notifyAll();
            }
        }
    }
}
public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
        new Thread(()->{
    
    
            syncWaitNotify.print(1,2,"a");
        }).start();
        new Thread(()->{
    
    
            syncWaitNotify.print(2,3,"b");
        }).start();
        new Thread(()->{
    
    
            syncWaitNotify.print(3,1,"c");
        }).start();

    }
}
  • Because wait/notify has only one waiting queue. However, multiple threads are involved, so using the boolean type cannot satisfy whether the currently awakened thread should run or wait, so we use an int type to determine - int flag
  • After our current thread has finished running, we need to know which thread's turn it is to run - int nextFlag

Lock condition variable version

class AwaitSignal extends ReentrantLock {
    
    
    // 循环次数
    private int loopNumber;
    public AwaitSignal(int loopNumber) {
    
    
        this.loopNumber = loopNumber;
    }
    public void start(Condition first) {
    
    
        this.lock();
        try {
    
    
            System.out.println("start");
            first.signal();
        } finally {
    
    
            this.unlock();
        }
    }
    public void print(String str, Condition current, Condition next) {
    
    
        for (int i = 0; i < loopNumber; i++) {
    
    
            this.lock();
            try {
    
    
                current.await();
                System.out.print(str);
                next.signal();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                this.unlock();
            }
        }
    }
}
public class Demo3 {
    
    
    public static void main(String[] args) {
    
    
        AwaitSignal as = new AwaitSignal(5);
        Condition aWaitSet = as.newCondition();
        Condition bWaitSet = as.newCondition();
        Condition cWaitSet = as.newCondition();
        new Thread(() -> {
    
    
            as.print("a", aWaitSet, bWaitSet);
        }).start();
        new Thread(() -> {
    
    
            as.print("b", bWaitSet, cWaitSet);
        }).start();
        new Thread(() -> {
    
    
            as.print("c", cWaitSet, aWaitSet);
        }).start();
        as.start(aWaitSet);
    }
}
  • Because our await and signal can obtain multiple waiting queues through Condition, then we can alternately wake up threads in different waiting queues.

Park Unpark Edition

class SyncPark {
    
    
    private int loopNum;
    private Thread[] threads;

    public SyncPark(int loopNum) {
    
    
        this.loopNum = loopNum;
    }

    public void setThreads(Thread... threads) {
    
    
        this.threads = threads;
    }
    public void print(String str){
    
    
        for (int i = 0; i < loopNum; i++) {
    
    
            LockSupport.park();
            System.out.println(str);
            LockSupport.unpark(nextThread());
        }
    }
    
    private Thread nextThread(){
    
    
        Thread current = Thread.currentThread();
        int index = 0;
        for (int i = 0; i < threads.length; i++) {
    
    
            if (current==threads[i]){
    
    
                index=i;
            }
        }
        if(index < threads.length - 1) {
    
    
            return threads[index+1];
        } else {
    
    
            return threads[0];
        }
    }
    public void start(){
    
    
        for (Thread thread : threads) {
    
    
            thread.start();
        }
        LockSupport.unpark(threads[0]);
    }

}
public class Demo4 {
    
    
    public static void main(String[] args) {
    
    
        SyncPark syncPark = new SyncPark(5);
        Thread t1 = new Thread(() -> {
    
    
            syncPark.print("a");
        });
        Thread t2 = new Thread(() -> {
    
    
            syncPark.print("b");
        });
        Thread t3 = new Thread(() -> {
    
    
            syncPark.print("c");
        });
        syncPark.setThreads(t1,t2,t3);
        syncPark.start();
    }
}

Guess you like

Origin blog.csdn.net/qq_50985215/article/details/131178873