ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java并发编程

2020-04-26 20:51:14  阅读:245  来源: 互联网

标签:Object Java 编程 Unsafe long 并发 线程 offset obj


Java 并发编程实践中的话:

 

编写正确的程序并不容易,而编写正常的并发程序就更难了。相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的。

 

并发编程相比 Java 中其他知识点学习起来门槛相对较高,学习起来比较费劲,从而导致很多人望而却步;而无论是职场面试和高并发高流量的系统的实现却还都离不开并发编程,从而导致能够真正掌握并发编程的人才成为市场比较迫切需求的。

 

本场 Chat 作为 Java 并发编程之美系列的高级篇之二,主要讲解内容如下:(建议先阅读 Java 并发编程之美:基础篇 )

 

rt.jar 中 Unsafe 类主要函数讲解, Unsafe 类提供了硬件级别的原子操作,可以安全的直接操作内存变量,其在 JUC 源码中被广泛的使用,了解其原理为研究 JUC 源码奠定了基础。

rt.jar 中 LockSupport 类主要函数讲解,LockSupport 是个工具类,主要作用是挂起和唤醒线程,是创建锁和其它同步类的基础,了解其原理为研究 JUC 中锁的实现奠定基础。

讲解 JDK8 新增原子操作类 LongAdder 实现原理,并讲解 AtomicLong 的缺点是什么,LongAdder 是如何解决 AtomicLong 的缺点的,LongAdder 和 LongAccumulator 是什么关系?

JUC 并发包中并发组件 CopyOnWriteArrayList 的实现原理,CopyOnWriteArrayList 是如何通过写时拷贝实现并发安全的 List?

二、 Unsafe 类探究

JDK 的 rt.jar 包中的 Unsafe 类提供了硬件级别的原子操作,Unsafe 里面的方法都是 native 方法,通过使用 JNI 的方式来访问本地 C++ 实现库。下面我们看下 Unsafe 提供的几个主要方法以及编程时候如何使用 Unsafe 类做一些事情。

 

2.1 主要方法介绍

long objectFieldOffset(Field field) 方法

作用:返回指定的变量在所属类的内存偏移地址,偏移地址仅仅在该 Unsafe 函数中访问指定字段时候使用。如下代码使用 unsafe 获取AtomicLong 中变量 value 在 AtomicLong 对象中的内存偏移

 

static {

        try {

            valueOffset = unsafe.objectFieldOffset

                (AtomicLong.class.getDeclaredField("value"));

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

}

int arrayBaseOffset(Class arrayClass) 方法 获取数组中第一个元素的地址

 

int arrayIndexScale(Class arrayClass) 方法 获取数组中单个元素占用的字节数

 

boolean compareAndSwapLong(Object obj, long offset, long expect, long update) 方法 比较对象 obj 中偏移量为 offset 的变量的值是不是和 expect 相等,相等则使用 update 值更新,然后返回 true,否者返回 false

 

public native long getLongVolatile(Object obj, long offset) 方法 获取对象 obj 中偏移量为 offset 的变量对应的 volatile 内存语义的值。

 

void putLongVolatile(Object obj, long offset, long value) 方法 设置 obj 对象中内存偏移为 offset 的 long 型变量的值为 value,支持 volatile 内存语义。

 

void putOrderedLong(Object obj, long offset, long value) 方法 设置 obj 对象中 offset 偏移地址对应的 long 型 field 的值为 value。这是有延迟的 putLongVolatile 方法,并不保证值修改对其它线程立刻可见。变量只有使用 volatile 修饰并且期望被意外修改的时候使用才有用。

 

void park(boolean isAbsolute, long time) 阻塞当前线程,其中参数 isAbsolute 等于 false 时候,time 等于 0 表示一直阻塞,time 大于 0 表示等待指定的 time 后阻塞线程会被唤醒,这个 time 是个相对值,是个增量值,也就是相对当前时间累加 time 后当前线程就会被唤醒。 如果 isAbsolute 等于 true,并且 time 大于 0 表示阻塞后到指定的时间点后会被唤醒,这里 time 是个绝对的时间,是某一个时间点换算为 ms 后的值。另外当其它线程调用了当前阻塞线程的 interrupt 方法中断了当前线程时候,当前线程也会返回,当其它线程调用了 unpark 方法并且把当前线程作为参数时候当前线程也会返回。

 

void unpark(Object thread) 唤醒调用 park 后阻塞的线程,参数为需要唤醒的线程。

 

下面是 Jdk8 新增的方法,这里简单的列出 Long 类型操作的方法

 

long getAndSetLong(Object obj, long offset, long update) 方法 获取对象 obj 中偏移量为 offset 的变量 volatile 语义的值,并设置变量 volatile 语义的值为 update。

public final long getAndSetLong(Object obj, long offset, long update)

  {

    long l;

    do

    {

      l = getLongVolatile(obj, offset);//(1)

    } while (!compareAndSwapLong(obj, offset, l, update));

    return l;

  }

从代码可知内部代码 (1) 处使用 getLongVolatile 获取当前变量的值,然后使用 CAS 原子操作进行设置新值,这里使用 while 循环是考虑到多个线程同时调用的情况 CAS 失败后需要自旋重试。

 

long getAndAddLong(Object obj, long offset, long addValue) 方法 获取对象 obj 中偏移量为 offset 的变量 volatile 语义的值,并设置变量值为原始值 +addValue。

public final long getAndAddLong(Object obj, long offset, long addValue)

  {

    long l;

    do

    {

      l = getLongVolatile(obj, offset);

    } while (!compareAndSwapLong(obj, offset, l, l + addValue));

    return l;

  }

类似 getAndSetLong 的实现,只是这里使用CAS的时候使用了原始值+传递的增量参数 addValue 的值。

 

2.2 如何使用 Unsafe 类

标签:Object,Java,编程,Unsafe,long,并发,线程,offset,obj
来源: https://www.cnblogs.com/ievizhwi/p/12782166.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有