1. 为啥有Locksuppot

看看Locksupport的源码中的注释可知,Locksupport是实现别的锁和同步类的基本原语。

Basic thread blocking primitives for creating locks and other synchronization classes.

该类native实现是依靠posix OS接口来搞的(pthread.h),类似以前操作系统课程学习的互斥锁。

class Parker : public os::PlatformParker {
private:
  volatile int _counter ;
  ...
public:
  void park(bool isAbsolute, jlong time);
  void unpark();
  ...
}
class PlatformParker : public CHeapObj<mtInternal> {
  protected:
    pthread_mutex_t _mutex [1] ;
    pthread_cond_t  _cond  [1] ;
    ...
}

我们再回到原来讨论的问题,为什么要有Locksupport,以前有Object.wait和notify还不够吗?看如下两个实现互斥资源访问的例子:
例1: 使用wait实现

// Thread1
synchronized (lock) {
    //这里使用while,避免虚假唤醒。因为并发时可能一起被唤醒,争夺锁也是有可能的,如果用if判断,则会走到后面的逻辑,互斥访问就失效了
    while (condition != true) {
        lock.wait()
    }
    // do stuff
}

// Thread2
synchronized (lock) {
    condition = true;
    lock.notify();
}

例2: 使用Loacksupport实现

// Thread1
while (condition != true) {
    LockSupport.park();
}

// do stuff

// Thread2
condition = true;
LockSupport.unpark(Thread1);

两者区别总结如下:

  1. Object.wait和notify都是针对对象的,notify实际上是不知道唤醒具体哪个线程的,而Locksupport支持指定线程唤醒
  2. 实现原理不同,Locksupport是基于Unsafe.park来实现的。具体可以见参考资料3
  3. Locksupport功能更加强大些: 基于“许可”的同步实现,提供parkBlocker来监视锁的持有等。而Object.wait方法来完成同步,需要依赖监视器锁。
  4. JDK1.6之后针对synchrnized引入了分级的锁,根据后面的代码示例发现两类同步原语的开销是差不多的

两者相同点:

  1. park和wait都会阻塞线程,释放锁
  2. 虽然响应中断行动不同,但是都会更改中断标志位
  3. 功能上其实相近,但是为了易用性和功能妥协,park和unpark基本可以替代Object.wait和notify等

从区别上来看可知,使用Locksupport能更加精细、灵活地控制线程的同步,利于实现各种同步工具和锁。精细体现在针对线程的同步控制,灵活体现在通过“许可”获取的方式来保证活性。LockSupport类的官方API中的描述基本概括了以上含义:

Methods park and unpark provide efficient means of blocking and unblocking threads that do not encounter the problems that cause the deprecated methods Thread.suspend and Thread.resume to be unusable for such purposes: Races between one thread invoking park and another thread trying to unpark it will preserve liveness, due to the permit. Additionally, park will return if the caller's thread was interrupted, and timeout versions are supported. The park method may also return at any other time, for "no reason", so in general must be invoked within a loop that rechecks conditions upon return. In this sense park serves as an optimization of a "busy wait" that does not waste as much time spinning, but must be paired with an unpark to be effective.

2. 代码示例

example1和example2比较了wait和park对中断的响应
example3和example4比较了两种同步原语的开销(实际结果是两种原语开销差不多)

package concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * Locksupport.park 和Object.wait的比较
 * Created by wanshao
 * Date: 2017/12/19
 * Time: 下午9:38
 **/
public class ParkVsWait {

    static Object shareObject = new Object();

    boolean condition = false;

    public static void main(String[] args) throws InterruptedException {
        ParkVsWait parkVsWait = new ParkVsWait();

        // example1和example2比较他们响应中断方式的不同
        //parkVsWait.example1();
        //parkVsWait.example2();
        // example3和example4比较两种同步原语的开销
        int MAX = 100000;
        Thread example3Worker;
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            example3Worker = new Thread(() -> {
                parkVsWait.example4();
            });
            example3Worker.start();
            example3Worker.join();
        }
        long end = System.currentTimeMillis();
        System.out.println("cost ms:" + (end - start));

    }

    // Example 1: Locksupport.park会更改标志位,但是不抛interruptException来响应中断
    private void example1() {
        Thread t = new Thread(() -> {
            //防止虚假唤醒
            while (condition != true) {

                LockSupport.park();
            }
            System.out.println("thread over." + Thread.currentThread().isInterrupted());
        });
        t.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        condition = true;
        // 中断线程
        t.interrupt();
    }

    // Example 2: Object.wait 会抛异常响应中断
    private void example2() {
        Thread t2 = new Thread(() -> {
            try {
                synchronized (shareObject) {
                    //防止虚假唤醒
                    while (condition != true) { shareObject.wait(3000); }
                }
            } catch (InterruptedException e) {
                System.out.println("Response interrupt");
                e.printStackTrace();
            }

        });
        t2.start();
        System.out.println("start to interrupt");
        t2.interrupt();
    }

    // Example 3: Object.wait 被唤醒需要获取锁
    private void example3() {
        Thread t2 = new Thread(() -> {
            try {
                //获取当前线程的监视器锁
                synchronized (shareObject) {
                    while (condition != true) {
                        System.out.println("start to wait");
                        shareObject.wait();
                    }
                }

            } catch (InterruptedException e) {
                System.out.println("Response interrupt");
                e.printStackTrace();
            }

        });
        t2.start();

        //唤醒在该锁上等待的线程
        synchronized (shareObject) {
            //System.out.println("start to notify");
            condition = true;
            shareObject.notifyAll();
        }
    }

    private void example4() {
        Thread t2 = new Thread(() -> {

            while (condition != true) {
                //System.out.println("start to wait");
                LockSupport.park();
            }

        });
        t2.start();

        //唤醒在该锁上等待的线程
        synchronized (shareObject) {
            //System.out.println("start to notify");
            condition = true;
            LockSupport.unpark(t2);

        }
    }
}

参考资料:

  1. Can LockSupport.park() replace Object.wait()?
  2. Unsafe.park vs Object.wait
  3. Java的LockSupport.park()实现分析