java.util.concurrent.locks.LockSupport (讲得比较细)
要学习JAVA中是如何实现线程间的锁,就得从LockSupport这个类先说起,因为这个类实现了底层的一些方法,各种的锁实现都是这个基础上发展而来的。这个类方法很少,但理解起来需要花费一点时间,因为涉及了很多底层的知识,这些都是我们平时不关心的。
上源代码:
package java.util.concurrent.locks;
import java.util.concurrent.*;
import sun.misc.Unsafe;
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
// Hotspot implementation via intrinsics API
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
static {
try {
parkBlockerOffset = unsafe.objectFieldOffset
(java.lang.Thread.class.getDeclaredField("parkBlocker"));
} catch (Exception ex) { throw new Error(ex); }
}
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
unsafe.putObject(t, parkBlockerOffset, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, 0L);
setBlocker(t, null);
}
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);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(true, deadline);
setBlocker(t, null);
}
public static Object getBlocker(Thread t) {
return unsafe.getObjectVolatile(t, parkBlockerOffset);
}
public static void park() {
unsafe.park(false, 0L);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
unsafe.park(false, nanos);
}
public static void parkUntil(long deadline) {
unsafe.park(true, deadline);
}
}
这个类提供的都是静态方法,且无法被实例化。
在LockSupport中有两个私有的成员变量:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
大家都知道JAVA语言是平台无关的,一次编译,可以在任何平台上运行,但是如果真的不可以调用一些平台相关的方法吗?其实unsafe类是可以做到的。
unsafe:是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。
parkBlokcerOffset:parkBlocker的偏移量,从字面上理解是这么个东东。但是parkBlocker又是干嘛的?偏移量又是做什么的呢?让我们来看看Thread类的实现:
//java.lang.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;
问题1:parkBlocker又是干嘛的?
原来java.lang.Thread的实现当中有这么一个对象。从注释上看,这个对象被LockSupport的setBlocker和getBlocker调用。查看JAVADOC会发现这么一段解释:

大致意思是,这个对象是用来记录线程被阻塞时被谁阻塞的。用于线程监控和分析工具来定位原因的。主要调用了LockSupport的getBlocker方法。
原来,parkBlocker是用于记录线程是被谁阻塞的。可以通过LockSupport的getBlocker获取到阻塞的对象。用于监控和分析线程用的。
问题2:偏移量又是做什么的?
static {
try {
parkBlockerOffset = unsafe.objectFieldOffset
(java.lang.Thread.class.getDeclaredField("parkBlocker"));
} catch (Exception ex) { throw new Error(ex); }
}
这个要往后看一下,原来偏移量就算Thread这个类里面变量parkBlocker在内存中的偏移量。
JVM的实现可以自由选择如何实现Java对象的“布局”,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之 类。sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来,它提供了objectFieldOffset()方法用于获取某个字段 相对Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个 Java对象的某个字段。
问题3:为什么要用偏移量来获取对象?干吗不要直接写个get,set方法。多简单?
仔细想想就能明白,这个parkBlocker就是在线程处于阻塞的情况下才会被赋值。线程都已经阻塞了,如果不通过这种内存的方法,而是直接调用线程内的方法,线程是不会回应调用的。
private static void setBlocker(Thread t, Object arg)
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
unsafe.putObject(t, parkBlockerOffset, arg);
}
参数:
Thread t 需要被赋值Blocker的线程
Object arg 具体的Blocker对象
解读:有了之前的理解,这个方法就很好理解了。对给定线程t的parkBlocker赋值。为了防止,这个parkBlocker被误用,该方法是不对外公开的。
public static Object getBlocker(Thread t)
public static Object getBlocker(Thread t) {
return unsafe.getObjectVolatile(t, parkBlockerOffset);
}
参数:Thread t, 被操作的线程对象
返回:parkBlocker对象
解读:从线程t中获取他的parkerBlocker对象。这个方法是对外公开的。
是不是可以利用这个方法来写一个监控程序,炫耀一把.
再讲其他几个方法之前,先谈谈park和unpark是做什么的.
看看SUN的官方解释 (点击查看源码)
/**
* Unblock the given thread blocked on <tt>park</tt>, or, if it is
* not blocked, cause the subsequent call to <tt>park</tt> not to
* block. Note: this operation is "unsafe" solely because the
* caller must somehow ensure that the thread has not been
* destroyed. Nothing special is usually required to ensure this
* when called from Java (in which there will ordinarily be a live
* reference to the thread) but this is not nearly-automatically
* so when calling from native code.
* @param thread the thread to unpark.
*
*/
public native void unpark(Object thread);
/**
* Block current thread, returning when a balancing
* <tt>unpark</tt> occurs, or a balancing <tt>unpark</tt> has
* already occurred, or the thread is interrupted, or, if not
* absolute and time is not zero, the given time nanoseconds have
* elapsed, or if absolute, the given deadline in milliseconds
* since Epoch has passed, or spuriously (i.e., returning for no
* "reason"). Note: This operation is in the Unsafe class only
* because <tt>unpark</tt> is, so it would be strange to place it
* elsewhere.
*/
public native void park(boolean isAbsolute, long time);
字面理解park,就算占住,停车的时候不就把这个车位给占住了么?起这个名字还是很形象的。unpark,占住的反义词,就是释放。把车从车位上开走。
翻译一下:
park:阻塞当前线程,(1)当配对的unpark发生或者(2)配对的unpark已经发生或者线程被中断时恢复(unpark先 行,再执行park)。 (3)当absolute是false时,如果给定的时间是非0(负数)或者给定的时间(正数, 时间单位时毫秒)已经过去了(0的时候会一直阻塞着)。(4)当Absolute是true时,如果给定的时间(时间单位是纳秒)过去了或者伪造的(在我 理解是参数不合法时)线程会恢复中断。这个操作是不安全的,所以在其他调用会很奇怪(奇怪?反正就是用的时候要小心)
unpark:当指定线程被park命令阻塞时unpark命令可以恢复阻塞。在park命令没有被先调用过的时候,调用unpark,线程仍然不被阻塞。(翻译的有点那个...).
理解一下,park与unpark命令是成对出现的。unpark必须要在park命令后执行。但是线程的恢复并不一定要用unpark, 因为park的时间参数,有些情况下线程会自己恢复。
public static void unpark(Thread thread)
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread);
}参数:Thread thread, 需要被中止挂起的线程
带blocker参数的park方法
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, 0L);
setBlocker(t, null);
} 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);
}
} public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(true, deadline);
setBlocker(t, null);
}参数:
Object blocker:用于记录到线程中的parkBlocker对象。
nanos:在nanos时间后线程自动恢复挂起
deadline:在deadline时刻线程自动(这个毫秒其实就是自1970年1月1日0时起的毫秒数)
解读:这三个方法其实是一个意思,把blocker放到线程当中,注意,这个park方法是一个阻塞的方法,除非4个条件
当配对的unpark发生或者
配对的unpark已经发生或者线程被中断时恢复(unpark先行,再执行park)
当absolute是false时,如果给定的时间是非0(负数)或者给定的时间(正数, 时间单位时毫秒)已经过去了(0的时候会一直阻塞着)。
当Absolute是true时,如果给定的时间(时间单位是纳秒)过去了或者伪造的(在我理解是参数不合法时)线程会恢复中断。
不带blocker参数的park方法
public static void park() {
unsafe.park(false, 0L);
} public static void parkNanos(long nanos) {
if (nanos > 0)
unsafe.park(false, nanos);
} public static void parkUntil(long deadline) {
unsafe.park(true, deadline);
}这三个方法跟上面一样,唯一区别是没有做parkBlocker的赋值操作。
来自我同事的并发编程网:
我们继续看一下JVM是如何实现park方法的,park在不同的操作 系统使用不同的方式实现,在linux下是使用的是系统方法pthread_cond_wait实现。实现代码在JVM源码路径src/os/linux /vm/os_linux.cpp里的 os::PlatformEvent::park方法,代码如下:
void os::PlatformEvent::park() {
int v ;
for (;;) {
v = _Event ;
if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;
}
guarantee (v >= 0, "invariant") ;
if (v == 0) {
// Do this the hard way by blocking ...
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
guarantee (_nParked == 0, "invariant") ;
++ _nParked ;
while (_Event < 0) {
status = pthread_cond_wait(_cond, _mutex);
// for some reason, under 2.7 lwp_cond_wait() may return ETIME ...
// Treat this the same as if the wait was interrupted
if (status == ETIME) { status = EINTR; }
assert_status(status == 0 || status == EINTR, status, "cond_wait");
}
-- _nParked ;
// In theory we could move the ST of 0 into _Event past the unlock(),
// but then we'd need a MEMBAR after the ST.
_Event = 0 ;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
}
guarantee (_Event >= 0, "invariant") ;
}
}
java.util.concurrent.locks.LockSupport (讲得比较细)的更多相关文章
- 013-并发编程-java.util.concurrent.locks之-AbstractQueuedSynchronizer-用于构建锁和同步容器的框架、独占锁与共享锁的获取与释放
一.概述 AbstractQueuedSynchronizer (简称AQS),位于java.util.concurrent.locks.AbstractQueuedSynchronizer包下, A ...
- Java并发—java.util.concurrent.locks包
一.synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 如果一个代码块被synchronized修饰了,当 ...
- synchronized 和 java.util.concurrent.locks.Lock 的异同 ?
主要相同点:Lock 能完成 synchronized 所实现的所有功能 主要不同点:Lock 有比synchronized 更精确的线程语义和更好的性能. synchronized 会自动释放锁,而 ...
- java基础知识回顾之java Thread类学习(八)--java.util.concurrent.locks(JDK1.5)与synchronized异同讲解
看API文档介绍几个方法: JDK1.5中提供了多线程的升级解决方案: 特点: 1.将同步synchronized显示的替换成Lock 2.接口Conditio ...
- java中的 java.util.concurrent.locks.ReentrantLock类中的lockInterruptibly()方法介绍
在java的 java.util.concurrent.locks包中,ReentrantLock类实现了lock接口,lock接口用于加锁和解锁限制,加锁后必须释放锁,其他的线程才能进入到里面执行, ...
- java中的 java.util.concurrent.locks.ReentrantLock类的使用方式
实现了lock的类为:ReentrantLock 接口的方式解释: lock()方法为获取锁对象,如果未获取到锁就一直获取锁. trylock():为布尔值,返回是否获取到了锁,如果没有获取到锁则返回 ...
- Synchronized和java.util.concurrent.locks.Lockde区别联系
1.Lock能够完成几乎所有synchronize的功能,并且具有锁投票,定时锁,可中断等候锁,synchronize是java语言层面的,是内置的关键字,Lock是一个包,synchronize使用 ...
- 简述synchronized和java.util.concurrent.locks.Lock的异同?
主要相同点:Lock能完成synchronized所实现的所有功能 . 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能.synchronized会自动释放锁,而Lock一 ...
- 12、java5锁java.util.concurrent.locks.Lock之ReentrantLock
JDK文档描述: public interface LockLock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作.此实现允许更灵活的结构,可以具有差别很大的属性,可 ...
随机推荐
- Sql Server中的事务隔离级别
数据库中的事物有ACID(原子性,一致性,隔离性,持久性)四个特性.其中隔离性是用来处理并发执行的事务之间的数据访问控制.SqlServer中提供了几种不同级别的隔离类型. 概念 Read UnCom ...
- Spark在Windows下的环境搭建
本文转载自:http://blog.csdn.net/u011513853/article/details/52865076 由于Spark是用Scala来写的,所以Spark对Scala肯定是原生态 ...
- 32位汇编基础_内存_每个应用进程都会有自己独立的4GB内存空间
1.每个应用进程都会有自己独立的4GB内存空间 这句话很多人听起来可能会很矛盾很不解. 例如,我的电脑只有2GB的内存,打开个软件机会占用4GB内存,而我的电脑内存只有2GB,显然不够用,但是为什么程 ...
- 移动自动化测试:Android Studio 、Appium、夜神模拟器
环境是Window 10 64位 第一章:安装Appium Appium和node.js需要一起安装,他们的依赖关系暂不深究. 1. node.js傻瓜式安装 官网地址:https://nodejs. ...
- TreeSet函数
TreeSet类的排序问题 TreeSet支持两种排序方法:自然排序和定制排序.TreeSet默认采用自然排序. 1.自然排序 TreeSet会调用集合元素的compareTo(Object ob ...
- Oracle 11.2.0.3.0 RAC GI_DB升级到11.2.0.4.0
转载: http://blog.csdn.net/frank0521/article/details/18226199 前言 还是大家常说的那句:生产环境千万记得备份哈~~~ 以下的环境,是我的测试 ...
- MySQL 存储配置
- [失败]SystemTap和火焰图(Flame Graph)
本文参考http://blog.51cto.com/xuclv/1184517 SystemTap简介: SystemTap provides free software (GPL) infrastr ...
- Java遍历一个目录下的所有文件
Java遍历一个目录下的所有文件 Java工具中为我们提供了一个用于管理文件系统的类,这个类就是File类,File类与其他流类不同的是,流类关心的是文件的内容,而File类关心的是磁盘上文件的存 ...
- 好记性不如烂笔头--linux学习笔记8关于nginx的动静分离
动静分离逻辑梳理 就是给nginx配置访问规则,不同后缀的文件访问不同的目录 worker_processes 1; events { worker_connections 1024; } http ...