https://www.cnblogs.com/lscz3633/p/7605427.html
Basic thread blocking primitives for creating locks and other synchronization classes.用来创建锁及其他同步类的基础线程阻塞原语。这是java doc中的解释,以下是一个先进先出 (first-in-first-out) 非重入锁类的框架。
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);
// Block while not first in queue or cannot acquire lock
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {//无法获取锁,则阻塞当前线程
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
上述代码简单实现的锁功能,主要使用了LockSupport的park和unpark阻塞和释放线程,AtomicBoolean的CAS操作来判断是否持有锁ConcurrentLinkedQueue来保存等待线程,此队列是一个线程安全的队列,当前仅当线程在等待队列队首且持有锁才会跳出while循环,从等待队列中移除。
locked.compareAndSet(false, true)的语意为,期望AtomicBoolean对象的值为false,并设置值为true,可以做为是否持有锁的判断
LockSupport源码
方法摘要 | |
---|---|
static Object | getBlocker(Thread t) 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。 |
static void | park() 为了线程调度,禁用当前线程,除非许可可用。 |
static void | park(Object blocker) 为了线程调度,在许可可用之前禁用当前线程。 |
static void | parkNanos(long nanos) 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。 |
static void | parkNanos(Object blocker, long nanos) 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。 |
static void | parkUntil(long deadline) 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。 |
static void | parkUntil(Object blocker, long deadline) 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。 |
static void | unpark(Thread thread) 如果给定线程的许可尚不可用,则使其可用。 |
LockSupport提供的全部为static修饰的静态方法,作为一个工具类在使用。主要提供了阻塞线程,和解除阻塞线程的方法。
// Hotspot implementation via intrinsics API
private static final Unsafe unsafe = Unsafe.getUnsafe(); //持有一个Unsafe的实例
private static final long parkBlockerOffset; //偏移量
static {
try {
parkBlockerOffset = unsafe.objectFieldOffset //初始化获取属性偏移量
(java.lang.Thread.class.getDeclaredField("parkBlocker"));
} catch (Exception ex) { throw new Error(ex); }
}
上一章中我们探讨过unsafe 的CAS用法,这里同样,LockSupport采用Unsafe类来获取属性修改属性,而且parkBlocker是Thread类的成员变量
/**
* The argument supplied to the current call to
* java.util.concurrent.locks.LockSupport.park.
* Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
* Accessed using java.util.concurrent.locks.LockSupport.getBlocker
*/
volatile Object parkBlocker;
同样是volatile修饰,意为阻塞者,阻塞当前线程的 对象,这个属性为我们提供了可以监控线程被阻塞的信息方法。
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
unsafe.putObject(t, parkBlockerOffset, arg);
}
/**
* Returns the blocker object supplied to the most recent
* invocation of a park method that has not yet unblocked, or null
* if not blocked. The value returned is just a momentary
* snapshot -- the thread may have since unblocked or blocked on a
* different blocker object.
*
* @param t the thread
* @return the blocker
* @throws NullPointerException if argument is null
* @since 1.6
*/
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return unsafe.getObjectVolatile(t, parkBlockerOffset);//根据属性偏移量获取属性值
}
为什么通过unsafe来获取线程的parkBlocker变量,而不是通过set和get方法获取?这是因为parkBlocker只有在线程被阻塞时才有意义,而此时set和get方法是无法通过线程对象调用的。
public native Object getObjectVolatile(Object var1, long var2);
public native void putObjectVolatile(Object var1, long var2, Object var4);//var2 为属性在var1中的偏移量,var4为要设置的属性值
这是Unsafe类提供的native方法,具体实现在这里不再深究。
上面探索了LockSupport中对阻塞者信息的处理,下面来看下阻塞及解除阻塞函数。
public static void park(Object blocker) {
Thread t = Thread.currentThread(); //当前线程
setBlocker(t, blocker); //设置被谁阻塞
unsafe.park(false, 0L); //阻塞线程
setBlocker(t, null); //线程解除阻塞,清空blocker信息
}
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread); //解除线程阻塞
}
这是Unsafe类中实质阻塞线程及解除线程阻塞的native函数,var1指明时间为绝对时间还是相对时间,false为相对时间,var2意为解除阻塞时间,设置为0则只有当线程中断,或调用unpark函数时解除锁定,若不为0,则等待var2时间后也会自动解除阻塞,注意这里的时间单位为纳秒;当var1位true时,为绝对时间,var2的时间单位为毫秒
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
最多等待nanos纳秒
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, nanos);
setBlocker(t, null);
}
}