Unsafe.park vs Object.wait

Bra*_*exe 17 java multithreading

我有几个问题Unsafe.parkObject.wait(及其相应的简历方法):

  1. 一般应该使用哪一个?
  2. 哪一个有更好的表现?
  3. 使用Unsafe.park结束有什么好处Object.wait吗?

Aja*_*jax 15

最有效的等待是LockSupport.park/unpark,不需要令人讨厌(直接)使用Unsafe,并且不支付重新同步线程的本地内存缓存.

这一点很重要; 你做的工作越少,效率越高.通过不同步任何东西,你不需要付费让你的线程检查主内存以获取来自其他线程的更新.

在大多数情况下,这不是你想要的.在大多数情况下,您希望线程看到"之前"发生的所有更新,这就是您应该使用Object.wait()和.notify()的原因,因为您必须同步内存状态才能使用它们.

LockSupport允许您在给定时间内安全地停放线程,并且只要没有其他线程试图取消停止,它将等待那么长时间(禁止虚假唤醒).如果您需要等待特定的时间,您需要重新检查截止日期并循环回park()直到该时间已经过去.

您可以使用它来有效地"睡眠",而无需另一个线程通过LockSupport.parkNanos或唤醒您.parkUntil(对于毫秒;两种方法都只是为您调用Unsafe).

如果你确实希望其他线程唤醒你,你很可能需要内存同步,并且不应该使用park(除非仔细编排没有竞争条件的易失性字段).

祝你好运,编码愉快!

  • 另外,要注意一个线程在技术上可以"虚假地取消",所以如果你需要使用超时至少等待nn nanos,你将需要监视该状态并重新进入停放状态,直到你的状态过去. (4认同)

Max*_*000 13

如果您是应用程序员,则不应该使用这些方法中的任何一种.

它们都是太低级别,容易搞砸并且不打算在库外使用.

为什么不尝试使用更高级别的构造,如java.util.concurrent.locks?

回答你的问题.park(...)直接在线程上工作.它将线程作为参数并将其置于休眠状态,直到在线程上调用unpark,除非已经调用了unpark.

它应该比Object.wait()更快,如果您知道需要阻塞/解除阻塞的线程,它将在监视器抽象上运行.

顺便说一下,如果从Java内部使用, unpark并不是那么不安全:

public native void unpark(Object thread)

取消阻止在停放时阻止的给定线程,或者,如果它未被阻止,则导致后续呼叫暂停不阻止.注意:此操作"不安全"仅仅是因为调用者必须以某种方式确保线程未被销毁.从Java调用时通常不需要特殊的东西(通常会有一个对线程的实时引用),但是当从本机代码调用时,这几乎不是自动的.

  • 是的,可能不会被使用,但是您会在线程转储中看到它们,因此最好了解它们的含义 (3认同)

Wan*_*eth 5

LockSupport.park/unpark 具有更好的性能,但它的 API 级别太低。

此外,它们有一些不同的操作,也许您应该注意到:

    Object lockObject = new Object();
    Runnable task1 = () -> {
        synchronized (lockObject) {
            System.out.println("thread 1 blocked");
            try {
                lockObject.wait();
                System.out.println("thread 1 resumed");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    };
    Thread thread1 = new Thread(task1);
    thread1.start();

    Runnable task2 = () -> {
        System.out.println("thread 2 running ");
        synchronized (lockObject) {
            System.out.println("thread 2 get lock");
            lockObject.notify();
        }
    };
    Thread thread2 = new Thread(task2);
    thread2.start();
Run Code Online (Sandbox Code Playgroud)

在这种情况下,线程 2 可以获得锁并通知线程 1 恢复,因为lockObject.wait();会释放锁。

    Object lockObject = new Object();
    Runnable task1 = () -> {
        synchronized (lockObject) {
            System.out.println("thread 1 blocked");
            LockSupport.park();
            System.out.println("thread 1 resumed");

        }
    };
    Thread thread1 = new Thread(task1);
    thread1.start();

    Runnable task2 = () -> {
        System.out.println("thread 2 running ");
        synchronized (lockObject) {
            System.out.println("thread 2 get lock");
            LockSupport.unpark(thread1);
        }
    };
    Thread thread2 = new Thread(task2);
    thread2.start();
Run Code Online (Sandbox Code Playgroud)

但是,如果这样使用LockSupport.park/unpark,则会导致死锁。因为 thread1 不会通过使用LockSupport.park. 因此,thread1 无法恢复。

所以要小心,除了阻塞线程之外,它们还有不同的行为。而实际上,还有一些类我们可以方便地使用它来协调多线程环境,比如CountDownLatch, Semaphore, ReentrantLock