Unsafe.park vs Object.wait

时间:2014-10-23 18:07:23

标签: java multithreading

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

  1. 一般应该使用哪一个?
  2. 哪一个有更好的表现?
  3. 使用Unsafe.park优于Object.wait
  4. 是否有任何好处

4 个答案:

答案 0 :(得分:14)

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

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

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

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

你可以用它来睡觉"有效率,没有其他线程必须通过LockSupport.parkNanos.parkUntil唤醒您(对于millis;这两种方法都只是为您调用Unsafe)。

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

祝你好运,编码愉快!

答案 1 :(得分:12)

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

它们的级别太低,容易搞砸,不适合在图书馆外使用。

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

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

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

顺便说一句,unpark并不是那么不安全if used from inside Java

  

public native void unpark(Object thread)

     

取消阻止给定的帖子   在公园被阻止,或者,如果没有被阻止,则导致后续呼叫   停车不要阻挡。注意:此操作不安全"完全是因为   调用者必须以某种方式确保线程没有被销毁。   调用时通常不需要特殊的东西来确保这一点   Java(通常会有一个实时引用   线程)但是这几乎不是自动的,所以在调用时   本地代码。

答案 2 :(得分:2)

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();

在这种情况下,线程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();

但是,如果您像这样使用LockSupport.park/unpark,将导致死锁。因为thread1不会使用LockSupport.park释放锁。因此,thread1无法恢复。

所以要小心,它们除了阻塞线程外还有其他行为。实际上,有些类可以方便地在多线程环境中使用它来进行协调,例如CountDownLatch, Semaphore, ReentrantLock

答案 3 :(得分:2)

如果您要管理synchronized块的并发性,则可以使用Object.waitnotifynotifyAll进行信令。这是Java支持的第一种并发控制,当时被认为非常易于使用。与周围的其他一切相比,肯定是这样。

但是,如今,java.util.concurrent中有很多类不需要太多专业知识来使用。这些是当今普通程序员应该使用的东西。

如果编写自己的无锁算法和数据结构,则将使用park*中的unparkLockSupport方法。它们是高性能的结构,不需要使用锁,并且经过精心设计,可以使这种工作尽可能简单……但这仍然是非常困难和棘手的工作,最好留下来给专家。