java.util.concurrent各组件分析 一 sun.misc.Unsafe

说到concurrent包也叫并发包,该包下主要是线程操作,方便的进行并发编程,提到并发那么锁自然是不可缺少的,包中的类存在了大量关于锁的操作

因此有必要先了解java中锁的原理,锁的底层就是sun.misc.Unsafe类,这个类可以说是java并发包的基础,基本上并发包的所有组件都是依赖Unsafe来进行底层同步操作的

java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe类提供了硬件级别的原子操作,在100+的方法中大部分都是native类型的,可以进行底层操作,
比如操作内存、低级同步、CAS方法、操作Class、操作Object等等。实际通过该类就像C、C++一样可以精确操作内存。

但java特点就是面向对象,表面上是不进行类似指针形式的直接内存操作,因此该类是不提倡我们在上层的代码中直接使用的。

1.简单知道可以操作内存
类中有3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法是对应的。
再深入就追踪到C的类库中了,不向下看了。

2.与并发编程相关的几类方法
2.1 CAS方法
2.2 通过getBooleanVolatile等方法,定位字段,进行存取
2.2 操作线程的方法,park线程等待,unpark唤醒线程

2.3 CAS方法,主要是compareAndSwapXXX方法
compareAndSwapObject提供了对一个对象引用进行CAS的能力
compareAndSwapInt提供了对一个32位整数进行CAS操作的能力
compareAndSwapLong提供了对64位整数进行CAS操作的能力

	/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
*
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

 

CAS操作看起来是很爽的,但是CAS操作会存在ABA问题,简单来说就是某线程进行compareAndSwap,在进行比较时,另一个线程对该内存值进行了两次或以上的更新操作,
但最终还原了值,这时对前一个线程来说相当于没变CAS操作成功。但是这个成功不代表没问题,如果值是对象、链表等其中的属性或某种状态是发生了变化的就有问题了。
解决ABA问题可以使用AtomicStampedReference/AtomicMarkableReference类进行,其带有数据版本号完全避免了ABA问题

2.2 通过getBooleanVolatile等方法,定位字段,进行存取

1.获取内存位置有staticFieldOffset、objectFieldOffset方法实现,方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的。

2.还可以通过下面的一系列,获取内存地址的方法

public native void putObjectVolatile(Object var1, long var2, Object var4);

    public native int getIntVolatile(Object var1, long var2);

    public native void putIntVolatile(Object var1, long var2, int var4);

    public native boolean getBooleanVolatile(Object var1, long var2);

    public native void putBooleanVolatile(Object var1, long var2, boolean var4);

    public native byte getByteVolatile(Object var1, long var2);

    public native void putByteVolatile(Object var1, long var2, byte var4);

    public native short getShortVolatile(Object var1, long var2);

    public native void putShortVolatile(Object var1, long var2, short var4);

    public native char getCharVolatile(Object var1, long var2);

    public native void putCharVolatile(Object var1, long var2, char var4);

    public native long getLongVolatile(Object var1, long var2);

    public native void putLongVolatile(Object var1, long var2, long var4);

    public native float getFloatVolatile(Object var1, long var2);

    public native void putFloatVolatile(Object var1, long var2, float var4);

    public native double getDoubleVolatile(Object var1, long var2);

    public native void putDoubleVolatile(Object var1, long var2, double var4);

    public native void putOrderedObject(Object var1, long var2, Object var4);

    public native void putOrderedInt(Object var1, long var2, int var4);

    public native void putOrderedLong(Object var1, long var2, long var4);

3.数组类型有BASE_OFFSET结尾的这些常量辅助去找位置
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;

2.3 操作线程的方法,park线程等待,unpark唤醒线程
park 让线程进入等待,并释放锁。调用后线程将一直阻塞知道超时或中断等条件出现。
unpark 唤醒等待的线程使其恢复正常。

在使用Unsafe时,我们需要Unsafe对象的实例。但Unsafe unsafe = new Unsafe()是不行的,其构造方法是私有的,它也有一个静态的getUnsafe()方法,
但如果直接调用Unsafe.getUnsafe(),会报SecurityException异常,只能从受信任的代码中使用这个方法。
这个受信任就比较麻烦,但Unsafe类包含一个私有的、名为theUnsafe的实例,可以通过Java反射等窃取该变量。
说这么多我们就会发现Unsafe在刻意隐藏,也就前面说的不推荐上传应用使用该类。

并发包内提供了LockSupport封装了Unsafe对象的类,一般我们都用这个封装类。

public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static final sun.misc.Unsafe UNSAFE;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.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 park() {
UNSAFE.park(false, 0L);
} public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
} public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
} public class Thread{
volatile Object parkBlocker;
}
主要包含的点:
1.利用Unsafe直接操作内存来设置blocker
parkBlocker是thread中的volatile类型的变量。UNSAFE.objectFieldOffset可以获得这里是blocker的内存偏移offset,即物理内存位置,和c++就一模一样了。
setBlocker方法是把arg对象设置到Thread的parkBlocker属性上。
parkBlocker是用来表明本线程是在哪个对象上阻塞 2.park有两个方法,带参数的就是指明锁对象,即表明该线程锁在那个对象上。不带参数就不表明了。 3.unpark直接唤醒

java.util.concurrent各组件分析 一 sun.misc.Unsafe的更多相关文章

  1. 原子类java.util.concurrent.atomic.*原理分析

    原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...

  2. java.util.concurrent包详细分析--转

    原文地址:http://blog.csdn.net/windsunmoon/article/details/36903901 概述 Java.util.concurrent 包含许多线程安全.测试良好 ...

  3. 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore

    前几篇分析了一下AQS的原理和实现.这篇拿Semaphore信号量做样例看看AQS实际是怎样使用的. Semaphore表示了一种能够同一时候有多个线程进入临界区的同步器,它维护了一个状态表示可用的票 ...

  4. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ...

  5. 谈论高并发(二十二)解决java.util.concurrent各种组件(四) 深入了解AQS(二)

    上一页介绍AQS其基本设计思路以及两个内部类Node和ConditionObject实现 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一) 这篇说一 ...

  6. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...

  7. 谈论高并发(三十)解析java.util.concurrent各种组件(十二) 认识CyclicBarrier栅栏

    这次谈话CyclicBarrier栅栏,如可以从它的名字可以看出,它是可重复使用. 它的功能和CountDownLatch类别似,也让一组线程等待,然后开始往下跑起来.但也有在两者之间有一些差别 1. ...

  8. java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

    在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...

  9. 聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源代码分析

    ThreadPoolExecutor是Executor运行框架最重要的一个实现类.提供了线程池管理和任务管理是两个最主要的能力.这篇通过分析ThreadPoolExecutor的源代码来看看怎样设计和 ...

随机推荐

  1. Spring 中AOP及前后置增强案例

    1.AOP面向切面编程 面向切面编程的本质:面向切面编程,指扩展功能不修改源代码,将功能代码从业务逻辑代码中分离出来.  1主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等.  2主要意 ...

  2. 可迭代对象(__iter__()和__next__())

    如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()和__next__()方法,该方法返回一个迭代对象 然后,Python的for循环就会不断调 ...

  3. Vs Code 2019软件安装教程及常用的入门设置

    小编认为VsCode是一款非常好用的编辑器,插件丰富,支持的语言种类非常多.我所使用VsCode主要打一些前端的代码,自己感觉very good. 点击运行. 按图所示操作. 安装教程很简单的,主要是 ...

  4. 由浅入深,讲解 spring 实战详细使用——spring 源码篇

    首先,这篇文章不会详细的深入底层源码,只是基于注解和配置来说说我们的 spring 的使用,别小看基础,保证有你没用过的注解和配置,走起. 我们先来建立一个 maven 项目,引入 spring 文件 ...

  5. D - Ugly Problem HDU - 5920

    D - Ugly Problem HDU - 5920 Everyone hates ugly problems. You are given a positive integer. You must ...

  6. ASP.NET Core 如何用 Cookie 来做身份验证

    前言 本示例完全是基于 ASP.NET Core 3.0.本文核心是要理解 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NE ...

  7. laravel项目本地数据库连接错乱原因和解决方法

    由于在本地建了两个laravel项目,test.me 和 api-test.me,当我在 test.me 中调用 curl 去请求 api-test.me 的方法,试图获取数据时, 接口一直返回没有找 ...

  8. C++ 运行时类别识别

    运行时动态类型的识别其实应该是多态方面的知识,这里我直接拿来单独成章. dynamic_cast和static_cast 1.static_cast用法如下: static_cast < Typ ...

  9. 基于 EntityFramework 生成 Repository 模式代码

    借助 WeihanLi.EntityFramework 实现简单的 Repository Intro 很多时候一些简单的业务都是简单的增删改查,动态生成一些代码完成基本的增删改查,而这些增删改查代码大 ...

  10. Python 爬取大众点评 50 页数据,最好吃的成都火锅竟是它!

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 胡萝卜酱 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...