美文网首页
10-AbstractQueuedSynchronizer(五)

10-AbstractQueuedSynchronizer(五)

作者: 鹏程1995 | 来源:发表于2020-02-05 09:32 被阅读0次

博客思路介绍

concurrent框架的思路在AQS中有不少体现。所以我们打算着重记录一下,记录的思路如下:

  1. 引入及介绍AQS队列通用的方法
  2. 介绍AQS预提供的各种和锁获得、释放相关的方法;及暴露出来的用来重写的方法。
  3. 介绍队列相关的监控方法
  4. 介绍Condition相关的方法
  5. 扩展、总结及展望

本文主要对AQS的基本操作支持方法做一些介绍,并对前四篇文章介绍的东西做一个总结。

基本支持方法介绍

这里主要介绍一些状态设置的方法,主要设计Unsafe类的使用,如果对此类不清楚,先看下面的扩展。

实现的思路很明确:

  1. 通过反射和Unsafe获得各个字段的偏移
  2. 然后调用UnsafeCAS操作即可【CAS->CompareAndSwapXXX

直接上源码得了:

/**
 * Setup to support compareAndSet. We need to natively implement
 * this here: For the sake of permitting future enhancements, we
 * cannot explicitly subclass AtomicInteger, which would be
 * efficient and useful otherwise. So, as the lesser of evils, we
 * natively implement using hotspot intrinsics API. And while we
 * are at it, we do the same for other CASable fields (which could
 * otherwise be done with atomic field updaters).
 */
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;

static {
    try {
        stateOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        headOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
        tailOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
        waitStatusOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("waitStatus"));
        nextOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("next"));

    } catch (Exception ex) { throw new Error(ex); }
}

/**
 * CAS head field. Used only by enq.
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
 * CAS tail field. Used only by enq.
 */
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

/**
 * CAS waitStatus field of a node.
 */
private static final boolean compareAndSetWaitStatus(Node node,
                                                     int expect,
                                                     int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                    expect, update);
}

/**
 * CAS next field of a node.
 */
private static final boolean compareAndSetNext(Node node,
                                               Node expect,
                                               Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

AQS总结

AQS作为java的concurrent包下的一个很重要的类,主要介绍了用来安排多线程并发的双向同步队列,并对条件同步队列的大部分方法做了基本实现。

其实AQS的实现大部分都是可读可理解的,它对线程挂起的恢复和线程间同步的操作直接或间接的依赖Unsafe类。【对线程的挂起恢复依赖LockSupport,但是LockSupport依赖Unsafe

我们在下面详细介绍读写锁、可重入锁时会根据情景继续理解AQS中剩余的问题。

扩展

Unsafe

讲在前面的话

这个类以看就不是正经类,连驼峰式命名都没有,估计是很早很基础的类了,这种类知道干啥的就行,不必涉猎过多。

类介绍

看了一下,感觉乱七八糟的,就直接刷博客了,介绍如下:

Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Cassandra、Hadoop、Kafka等。Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。

借用网上一个图

1.jpg

Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。

类用法

初始化

Unsafe类使用了单例模式,他的构造函数为privete,需要通过一个静态方法getUnsafe()来获取。但Unsafe类做了限制,如果是普通的调用的话,它会抛出一个SecurityException异常;只有由主类加载器加载的类才能调用这个方法。

源码如下:

public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

我们如果要在自己的类中使用,可以通过如下方法获得:

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

功能介绍

Unsafe类提供了以下这些功能:

内存管理

包括分配内存、释放内存等。

该部分包括了allocateMemory(分配内存)、reallocateMemory(重新分配内存)、copyMemory(拷贝内存)、freeMemory(释放内存 )、getAddress(获取内存地址)、addressSize、pageSize、getInt(获取内存地址指向的整数)、getIntVolatile(获取内存地址指向的整数,并支持volatile语义)、putInt(将整数写入指定内存地址)、putIntVolatile(将整数写入指定内存地址,并支持volatile语义)、putOrderedInt(将整数写入指定内存地址、有序或者有延迟的方法)等方法。getXXX和putXXX包含了各种基本类型的操作。

利用copyMemory方法,我们可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现clone方法,当然这通用的方法只能做到对象浅拷贝。

非常规的对象实例化

allocateInstance()方法提供了另一种创建实例的途径。通常我们可以用new或者反射来实例化对象,使用allocateInstance()方法可以直接生成对象实例,且无需调用构造方法和其它初始化方法。

这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。

操作类、对象、变量

这部分包括了staticFieldOffset(静态域偏移)、defineClass(定义类)、defineAnonymousClass(定义匿名类)、ensureClassInitialized(确保类初始化)、objectFieldOffset(对象域偏移)等方法。

通过这些方法我们可以获取对象的指针,通过对指针进行偏移,我们不仅可以直接修改指针指向的数据(即使它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。

数组操作

这部分包括了arrayBaseOffset(获取数组第一个元素的偏移地址)、arrayIndexScale(获取数组中元素的增量地址)等方法。arrayBaseOffset与arrayIndexScale配合起来使用,就可以定位数组中每个元素在内存中的位置。

由于Java的数组最大值为Integer.MAX_VALUE,使用Unsafe类的内存分配方法可以实现超大数组。实际上这样的数据就可以认为是C数组,因此需要注意在合适的时间释放内存。

多线程同步

包括锁机制、CAS操作等。

这部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。

其中monitorEnter、tryMonitorEnter、monitorExit已经被标记为deprecated,不建议使用。

Unsafe类的CAS操作可能是用的最多的,它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。这是一种乐观锁,通常认为在大部分情况下不出现竞态条件,如果操作失败,会不断重试直到成功。

挂起与恢复

这部分包括了park、unpark等方法。

将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

内存屏障

这部分包括了loadFence、storeFence、fullFence等方法。这是在Java 8新引入的,用于定义内存屏障,避免代码重排序。

loadFence() 表示该方法之前的所有load操作在内存屏障之前完成。同理storeFence()表示该方法之前的所有store操作在内存屏障之前完成。fullFence()表示该方法之前的所有load、store操作在内存屏障之前完成。

注意

知道就好。。。。。。。。后面用到时再看

LockSupport

大概看了一下,主要是依赖Unsafe完成一些对线程的阻塞和唤醒操作,并做了一些多样化的封装【比如加入了各种市时限的入参】,就不详细介绍了,要不收不住手了。

参考文献

Unsafe

由于对此类无从下手,也不清楚从哪里看,故在扩展——Unsafe中大量粘贴了pkufork的博客成品,链接如下:

https://www.cnblogs.com/pkufork/p/java_unsafe.html

相关文章

  • 10-AbstractQueuedSynchronizer(五)

    博客思路介绍 concurrent框架的思路在AQS中有不少体现。所以我们打算着重记录一下,记录的思路如下: 引入...

  • 五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张

    五张试一下五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张...

  • 五张

    五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五张五...

  • 五触,五感,五识,五受、五觉

    从外尘到了我们的眼,就是五触,这里会有一瞬时记忆,包括眼中的记忆,耳朵中的记忆,及 从五触到大脑的红色部分,叫着五...

  • 五  月  五

    今夜,城市的灯光淹没了月牙儿, 我坐在窗台看街前的树叶摆动, 有一丝风透过纱窗, 一点微凉, 散懒的云飘着几朵, ...

  • 五块五

    回到家后 奶奶说 买馒头找了15.5 小孩子说拿了五毛买东西 可不见了五块 奶奶就去小卖部问老板 小孩拿了几块去买...

  • 训练五(五)

    正是那些军需官把人打造成陆 军士兵、水兵及陆战队员。在他们面前, 我们一丝不挂。随着身上的衣服一件件脱 去,个性也...

  • 五钝/五利/五盖/五蕴

    五钝(五毒心) 因为有了它们的存在,修行人的本心本觉将会被遮蔽,就不可能明心见性。 一、贪(财、色、名、食、睡) ...

  • 五行、 五脏、五腑、 五官、 五华、 五味、五色、 五情、五液和

    五行是相邻相生,相隔相克 相生:木生火,火生土,土生金,金生水,水生木。----生为发展 相克:木克土,土克水,水...

  • 五行探源与发展……六愚

    (一)五行探源與發展: 1、五行思想探源: 「五行」,亦稱五節、五辰、五時、五氣、五常、五部、五運等。 當代人們皆...

网友评论

      本文标题:10-AbstractQueuedSynchronizer(五)

      本文链接:https://www.haomeiwen.com/subject/womjxhtx.html