Java concurrency: Suspend and wake up threads LockSupport tool class detailed

LockSupport overview

The LockSupport tool class defines a set of public static methods that provide the most basic thread blocking and wake-up functions. It is the basis for creating locks and other synchronization classes. You will find that the place to block threads and wake up threads in AQS is to use LockSupport The provided park and unpark methods, such as the following paragraph:

// 挂起线程
	private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
	// 唤醒线程
    private void unparkSuccessor(Node node) {
		//...
        if (s != null)
            LockSupport.unpark(s.thread);
    }

Park and unpark related methods

LockSupport provides a set of methods starting with park to block the current thread [omit static]:

void park(): Block the current thread. If the unpark(Thread thread) method is called or the current thread is interrupted, it can return from the park() method.

void parkNanos(long nanos): Block the current thread, no longer than nanos nanoseconds. The return condition adds a timeout return on the basis of park().

void parkUntil(long deadline): Block the current thread until the deadline [the number of milliseconds from 1970 to the deadline] time.

void unpark(Thread thread): Wake up the thread thread in a blocked state.

In JDK1.6, several methods with blocker parameters have been added. The blocker parameter is used to identify the object that the current thread is waiting for for troubleshooting and system monitoring.

The following demonstrates the use of park() and unpark() methods:

Call the park() method in the thread thread. By default, the thread does not hold a license, so it will be blocked and suspended. The unpark(thread) method will allow the thread thread to obtain a license before returning from the park() method.

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

        Thread thread = new Thread(() ->{
            String name = Thread.currentThread().getName();
            System.out.println(name + " begin park");
            LockSupport.park();// 如果调用park的线程已经获得了关联的许可证,就会立即返回
            System.out.println(name + " end park");
        },"A");
        thread.start(); // 默认情况下,thread不持有许可证,会被阻塞挂起

        Thread.sleep(1000); 

        System.out.println(thread.getName() + " begin unpark");

        LockSupport.unpark(thread);//让thread获得许可证

    }// 结果如下
A begin park
A begin unpark
A end park

You need to understand the role of licenses in here, we can also do give the thread a license, then when the park will not be blocked up.

public static void main(String[] args) {
        System.out.println("begin park");
        // 使当前线程获得许可证
        LockSupport.unpark(Thread.currentThread());
        // 再次调用park方法,因为已经有许可证了,不会被阻塞
        LockSupport.park();
        System.out.println("end park");
    }// 结果如下
begin park
end park

Interrupt presentation

When the thread is interrupted, the park method will not throw an exception, so the interruption status needs to be processed after the park exits.

public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " begin park");
            // 一直挂起自己,只有被中断,才会推出循环
            while (!Thread.currentThread().isInterrupted()) {
                LockSupport.park();
            }
            System.out.println(name + " end park");
        }, "A");
        thread.start();
        Thread.sleep(1000);
        System.out.println("主线程准备中断线程" + thread.getName());
        // 中断thread
        thread.interrupt();
    }// 结果如下
A begin park
主线程准备中断线程A
A end park

The role of blocker

Starting from JDK 1.6, a series of park methods began to support passing in blocker parameters to identify the object that the current thread is waiting for. When the thread calls the park method without a license and is blocked and suspended, the blocker object will be blocked. Log into the thread.

public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker); // 设置blocker
        UNSAFE.park(false, 0L);
        setBlocker(t, null); // 清除blocker
    }

There is a volatile Object parkBlocker variable in the Thread class, which is used to store the blocker object passed by the park method, that is, the blocker variable is stored in the member variable of the thread that calls the park method.

Next, let's feel it through two examples:

Test without blocker

public class TestParkWithoutBlocker {
    public void park(){
        LockSupport.park();
    }

    public static void main(String[] args) throws InterruptedException {
        new TestParkWithoutBlocker().park();
        Thread.sleep(3000);
    }
}

Use the jps command to list the currently running process 4412 TestPark, and then use the jstack 4412 command to view the thread stack:

Java concurrency: Suspend and wake up threads LockSupport tool class detailed

 

Test with blocker

public class TestBlockerPark {

    public void park(){
        LockSupport.park(this); // 传入blocker = this
    }

    public static void main(String[] args) throws InterruptedException {
        new TestBlockerPark().park();
        Thread.sleep(3000);
    }
}

 

Java concurrency: Suspend and wake up threads LockSupport tool class detailed

 

The obvious difference is that using the park method with the blocker parameter, you can see the information about the specific blocking object through jstack:

- parking to wait for  <0x000000076b77dff0> (a chapter6_1_LockSupport.TestBlockerPark)

The diagnostic tool can call the getBlocker(Thread) method to obtain the blocker object. The JDK recommends that we use the park method with the blocker parameter, and set the blocker to this, so that we can know which class is blocked when troubleshooting the problem with the print thread stack .

Demo provided by JDK

Old tradition, excerpt a use case on JavaDoc:

/**
 * 先进先出的锁,只有队列的首元素可以获取锁
 */
class FIFOMutex {
    private final AtomicBoolean locked = new AtomicBoolean(false);
    private final Queue<Thread> waiters
            = new ConcurrentLinkedQueue<Thread>();

    public void lock() {
        // 中断标志
        boolean wasInterrupted = false; 
        Thread current = Thread.currentThread();
        waiters.add(current);

        // 不是队首线程 或 当前锁已经被其他线程获取,则调用park方法挂起自己
        while (waiters.peek() != current ||
                !locked.compareAndSet(false, true)) {
            LockSupport.park(this);
            // 如果park方法是因为被中断而返回,则忽略中断,并且重置中断标志
            // 接着再次进入循环
            if (Thread.interrupted()) // ignore interrupts while waiting
                wasInterrupted = true;
        }
        
        waiters.remove();
        // 如果标记为true,则中断线程
        // [虽然我对中断信号不感兴趣,忽略它,但是不代表其他线程对该标志不感兴趣,因此恢复一下.]
        if (wasInterrupted)          // reassert interrupt status on exit
            current.interrupt();
    }

    public void unlock() {
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    }
}

to sum up

LockSupport provides static methods for threads to suspend park and wake up unpark.

After JDK1.6, blocker objects are allowed to be passed in, which is convenient for problem monitoring and troubleshooting.

If the thread of the park is interrupted, no exception will be thrown, and you need to handle the interruption status yourself.

Original link: https://www.cnblogs.com/summerday152/p/14290036.html

If you think this article is helpful to you, you can follow my official account and reply to the keyword [Interview] to get a compilation of Java core knowledge points and an interview gift package! There are more technical dry goods articles and related materials to share, let everyone learn and progress together!

Guess you like

Origin blog.csdn.net/weixin_48182198/article/details/112776091
Recommended