[java并发] java高并发系列 - 第22天:JUC底层工具类Unsafe,高手必须要了解

java高并发系列 - 第22天:JUC底层工具类Unsafe 最近我们一直在学习java高并发,java高并发中主要涉及到类位于java.util.concurrent包中,简称juc,juc中大部分类都是依赖于Unsafe来实现的,主要用到了Unsafe中的CAS、线程挂起、线程恢复等相关功能。所以如果打算深入了解JUC原理的,必须先了解一下Unsafe类。先上一幅Unsafe类的功能图: 阅读详情

原文链接:查看原文

感谢公众号“ 路人甲Java”的分享,如有冒犯,请联系删除,快去关注他吧
在这里插入图片描述

本文主要内容:

  1. Unsafe基本介绍
  2. 获取Unsafe实例
  3. Unsafe中的CAS操作
  4. Unsafe中原子操作相关方法介绍
  5. Unsafe中线程调度相关方法介绍
  6. park和unpark示例
  7. Unsafe锁示例
  8. Unsafe中对volatile的支持

基本介绍

java高并发中主要涉及到的类位于 java.util.concurrent 包中,简称juc,juc中大部分类都是依赖于Unsafe来实现的,主要用到了Unsafe中的CAS、线程挂起、线程恢复等相关功能。所以如果打算深入了解JUC原理的,必须先了解一下Unsafe类。

先看一张图:

在这里插入图片描述

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确的使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

从Unsafe功能图上来看,Unsafe提供的API大致可分为 内存操作、CAS 、Class相关、对象操作 、 线程调度 、系统信息获取 、 内存屏障 、 数组操作 等几类,本文主要介绍3个常用的操作:CAS、线程调度和对象操作。

看一下Unsafe的部分源码:

public final class Unsafe{
	//单例对象
	public static final Unsafe theUnsafe;

	private Unsafe(){
		
	}

	@CallerSensitive
	public static Unsafe getUnsafe(){
		Class var0 = Reflection.getCallerClass();
		//仅在引导器类加载器`BootStrapClassLoader` 加载时才合法
		if(!VM.isSystemDomainLoader(var0.getClassLoader)){
			throw new SecurityException("Unsafe");
		} else{
			return theUnsafe;
		}
	}
}

从代码中可以看出,Unsafe类为单例实现,提供静态方法getUnsafe获取Unsafe实例,内部会判断当前调用者是否是由系统类加载器加载的,如果不是系统类加载器加载的,会抛出 SecurityException 异常。

那我们想使用这个类,如何获取呢?

可以把这个类放在jdk的lib目录下,那么启动时就会自动加载,这种方式不是很好。

我们学过反射,通过反射可以获取到 Unsafe 中的 theUnSafe 字段的值,这样可以获取到Unsafe对象的实例。


通过反射获取Unsafe实例

代码如下:

package aboutThread.Concurrent.Day22;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class Demo1 {
    static Unsafe unsafe;

    static{
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        System.out.println(unsafe);
    }
}

输出:

sun.misc.Unsafe@5a07e868

Unsafe中的CAS操作

看一下Unsafe中的CAS相关方法定义:

/**
 * CAS 操作
 *
 * @param o        包含要修改field的对象
 * @param offset   对象中某field的偏移量
 * @param expected 期望值
 * @param update   更新值
 * @return true | false
 */
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);

public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);

public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);

什么是CAS? 即比较并替换,实现并发算法时常用的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作时,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不作任何操作,多个线程同时执行cas操作,只有一个成功。 我们知道,CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致的问题,Unsafe提供的CAS方法(如compareAndSwapXXX)底层实现即为CPU指令cmpxchg。执行cmpxchg指令的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会执行cas操作,也就是说CAS的原子性实际上是CPU实现的,其实在这一点上还是有排他锁的,只是比起用synchronized,这里的排他时间要短的多,所以在多线程下性能会比较好。

说一下offset,offset为字段的偏移量,每个对象有个地址,offset是字段相对于对象地址的偏移量,对象地址记为baseAddress,字段偏移量记为offset,那么字段对应的实际地址就是baseAddress+offset ,所以cas通过对象、偏移量就可以去操作字段对应的值了。

CAS在java.util.concurrent.atomic相关类、Java AQS、JUC中并发集合等实现上有非常广泛的应用,我们看一下 java.util.concurrent.atomic.AtomicInteger 类,这个类可以在多线程环境中对int类型的数据执行高效的原子修改操作,并保证数据的正确性,看一下此类中用到的Unsafe cas的地方:

在这里插入图片描述

在这里插入图片描述

JUC中其他用到CAS的地方就不列举了,看源码去喽!


Unsafe中原子操作相关方法介绍

/**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object {@code o}
     * at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    @ForceInline
    public final int getAndAddInt(Object o, long offset, int delta) {
        return theInternalUnsafe.getAndAddInt(o, offset, delta);
    }

    /**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object {@code o}
     * at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    @ForceInline
    public final long getAndAddLong(Object o, long offset, long delta) {
        return theInternalUnsafe.getAndAddLong(o, offset, delta);
    }

    /**
     * Atomically exchanges the given value with the current value of
     * a field or array element within the given object {@code o}
     * at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    @ForceInline
    public final int getAndSetInt(Object o, long offset, int newValue) {
        return theInternalUnsafe.getAndSetInt(o, offset, newValue);
    }

    /**
     * Atomically exchanges the given value with the current value of
     * a field or array element within the given object {@code o}
     * at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    @ForceInline
    public final long getAndSetLong(Object o, long offset, long newValue) {
        return theInternalUnsafe.getAndSetLong(o, offset, newValue);
    }

    /**
     * Atomically exchanges the given reference value with the current
     * reference value of a field or array element within the given
     * object {@code o} at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param newValue new value
     * @return the previous value
     * @since 1.8
     */
    @ForceInline
    public final Object getAndSetObject(Object o, long offset, Object newValue) {
        return theInternalUnsafe.getAndSetReference(o, offset, newValue);
    }


看一下上面的方法,内部通过自旋的CAS操作实现的,这些方法都可以保证操作的数据在多线程环境下的原子性,正确性。

来个示例,我们还是来实现一个网站计数功能,同时有100个人发起对网站的请求,每个人发起10次请求,每次请求算一次,最终结果是1000次,代码如下:

package aboutThread.Concurrent.Day22;

import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import sun.misc.Unsafe;

/**
 * @author Aran
 */
public class Demo2 {
    static Unsafe unsafe;
    //用来记录网站访问量,每次访问+1
    static int count;
    //count在Demo.class对象中的地址偏移量
    static long countOffset;

    static {
        try {
            //获取Unsafe对象
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            Field countField = Demo2.class.getDeclaredField("count");
            //获取count字段在Demo2中的内存地址的偏移量
            countOffset = unsafe.staticFieldOffset(countField);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //模拟访问一次
    public static void request() throws InterruptedException {
        //模拟耗时5毫秒
        TimeUnit.MILLISECONDS.sleep(5);
        //对count原子加1
        unsafe.getAndAddInt(Demo2.class, countOffset, 1);
    }

    public static void main(String[] args) throws InterruptedException {
        long starTime = System.currentTimeMillis();
        int threadSize = 100;
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(() -> {
                try {
                    for (int j = 0; j < 10; j++) {
                        request();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            });
            thread.start();
        }

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + ",耗时:" + (endTime - starTime) + ",count=" + count);
    }
}

输出:

main,耗时:90,count=1000

代码中我们在静态块中通过反射获取到了Unsafe类的实例,然后获取Demo2中count字段内存地址偏移量 countOffset ,main方法中模拟了100个人,每人发10次请求,等到所有请求完毕之后,输出count结果。

代码中用到 CountDownLath ,通过countDownLatch.await()让主线程等待,等待100个子线程都执行完毕后,主线程在进行运行。CountDownLatch 的使用可以参考第16天的文章


Unsafe中线程调度的相关方法

这部分,包括线程挂起,恢复,锁机制等方法

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程,isAbsolute:是否是绝对时间,如果为true,time是一个绝对时间,如果为false,time是一个相对时间,time表示纳秒
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);

调用park 后,线程将被阻塞到,直到 unpark 调用或者超时,如果之前调用过umpark ,不会进行阻塞,即 park 和 unpark 不区分先后顺序,后面三个方法已过期,不建议使用。


park和unpark实例

package aboutThread.Concurrent.Day22;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

import sun.misc.Unsafe;

public class Demo3 {
    static Unsafe unsafe;

    static{
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe)field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 调用park和unpark,模拟线程的挂起和唤醒
     * 
     * @throws InterruptedException
     */
    public static void m1() throws InterruptedException{
        Thread thread = new Thread(() ->{
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ":start!");
            unsafe.park(false, 0);
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ":end!");
        });
        thread.setName("t1");
        thread.start();

        TimeUnit.SECONDS.sleep(5);
        unsafe.unpark(thread);
    }

    /**
     * 阻塞指定的时间
     */
    public static void m2(){
        Thread thread = new Thread(() ->{
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ":start!");
            //线程挂起3秒
            unsafe.park(false, TimeUnit.SECONDS.toNanos(3));
            System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ":end!");
        });
        thread.setName("t2");
        thread.start();
    }


    public static void main(String[] args) throws InterruptedException{
        m1();
        m2();
    }
}

输出:

1591759231159,t1:start!
1591759236163,t1:end!
1591759236164,t2:start!
1591759239169,t2:end!

m1()中t1调用park方法,park方法会将 当前线程阻塞,被阻塞5秒之后,被主线程调用unpark方法给唤醒了,unpark方法参数表示需要唤醒的线程。

线程中相当于有个许可,许可默认为0,调用park的时候,发现是0会阻塞当前线程,调用unpark之后,许可会被置为1,并会唤醒当前线程。如果在park之前先调用了unpark方法,执行park方法的时候,不会阻塞。park方法被唤醒之后,许可又会被置为0,多次调用unpark的效果是一样的,许可还是1。

juc中 LockSupport 类是通过unpark和park方法实现的,需要了解的同学,可以移步:第14天的文章


Unsafe 锁示例

package aboutThread.Concurrent.Day22;

import java.lang.reflect.Field;
import java.util.Currency;
import java.util.concurrent.CountDownLatch;

import sun.misc.Unsafe;

public class Demo4 {
    static Unsafe unsafe;
    //用来记录网站的访问量,每次访问+1
    static int count;
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe)field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 模拟访问一次
     */
    @Deprecated
    public static void request(){
         unsafe.monitorEnter(Demo4.class); 
         try {
             count++;
         } catch (Exception e) {
             unsafe.monitorExit(Demo4.class);
         }  
    }

    public static void main(String[] args) throws InterruptedException{
        long startTime = System.currentTimeMillis();
        int threadSize = 100;
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(() ->{
                try {
                    for(int j = 0; j < 10;j++){
                        request();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            });
            thread.start();
        }
        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + ",耗时:" + (endTime - startTime) + ",count="+ count);
    }
}

输出:

我的环境没有运行起来,找不到这个方法,难道是我的环境有问题

main,耗时:64,count=1000

注意:

  1. monitorEnter、monitorExit、tryMonitorEnter 3个方法已过期,不建议使用了
  2. monitorEnter、monitorExit必须成对出现,出现的次数必须一致,也就是说锁了n次,也必须释放n次,否则会造成死锁

Unsafe中保证变量的可见性

关于变量可见性需要先了解Java内存模型,可以移步到:

第四天的文章
第七天的文章

java中操作内存分为主内存和工作内存,共享数据在主内存中,线程如果需要操作主内存的数据,需要先将主内存的数据复制到线程独有的工作内存中,操作完成之后再将其刷新到主内存中。如果线程A要想看到线程B修改后的数据,需要满足:线程B修改完成后,需要将数据偶从自己的工作内存中刷新到主内存中,并且A需要去主内存中读取数据。

被关键字volatile修饰的数据:

  1. 如果一个变量被volatile修饰,读取这个变量的时候,会强制从主内存中读取,然后将其复制到当前线程的工作内存中使用
  2. 给volatile修饰的变量赋值的时候,会强制将其赋值的结果从工作内存中刷新到主内存中

上面两点语义保证了被volatile修饰的数据在多线程中的可见性。

Unsafe中提供了volatile语义一样的功能的方法,如下:

//设置给定对象的int值,使用volatile语义,即设置后立马更新到内存对其他线程可见
public native void  putIntVolatile(Object o, long offset, int x);
//获得给定对象的指定偏移量offset的int值,使用volatile语义,总能获取到最新的int值。
public native int getIntVolatile(Object o, long offset);

putIntVolatile方法,2个参数:

o:表示需要操作的对象
offset:表示操作对象中的某个字段地址偏移量
x:将offset对应的字段的值修改为x,并且立即刷新到主内存中


调用这个方法,会强制将工作内存中修改的数据刷新到主内存中

getIntVolatile方法,2个参数:

o:表示需要操作的对象
offset:表示操作对象中的某个字段地址偏移量


每次调用这个方法都会强制从主内存中读取值,将其复制到工作内存中使用

其他的还有几个putXXXVolatile、getXXXVolatile方法和上面的类似。


这是java并发学习的第22天,加油!

点击率预测的贝叶斯平滑 概述 电商领域中经常需要计算或预测一些转化率指标,如最典型的CTR(点击率,Click-Through Rate)。这些转化率可以是模型的预测值,也可以作为模型的特征(feature)使用。以商品点击率预测为例,CTR的值等于点击量(Click)除以曝光量(Impression或Exposure)。以rr表示点击率, r=CI(1)r=\frac{C}{I} \tag{1}. 但在实际应 阅读详情

相关推荐

告别昂贵硬件!用Unity3D 2023 + ROS2 Humble搭建你的第一个SLAM仿真实验室(保姆级教程)

本文提供了一份详细的教程,介绍如何使用Unity3D 2023和ROS2 Humble搭建SLAM仿真实验室,无需昂贵硬件即可实现虚拟传感器模拟和算法测试。通过Docker容器配置、ROS2安装优化及Unity插件集成,开发者可以快速构建高精度的仿真环境,适用于自主导航、多传感器标定等场景。

weixin_42526353的博客 433

Java和C++中的volatile

1.Java中的volatile:在Java内存模型中,线程共享的资源放在主存中,每个线程同时拥有自己的本地内存。而本地内存中存放了被该线程使用到的主内存变量的拷贝。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。由此可能导致线程间无法读取变量的最新状态。被volatile修饰的变量在修改时会被强制写到主存中,从而保证该变量对其他线程的可见性。 2.C++中的volat...

DK233333的博客 1035

ArcGIS | 全球新冠肺炎地图数据下载和处理

ArcGIS

thinkpang的博客 2900

java高并发系列 - 第22天:JUC底层工具类Unsafe,高手必须要了解

这是java高并发系列第22篇文章,文章基于jdk1.8环境。本文主要内容Unsafe基本介绍获取Unsafe实例Unsafe中的CAS操作Unsafe中原子操作相关方法...

路人甲Java 843

GlobalMapper地形对比与方量计算实战:从两期数据到填挖方区域精准提取

本文详细介绍了使用GlobalMapper进行地形对比与方量计算的实战方法,从两期数据准备到填挖方区域精准提取。通过三步核心操作和实用技巧,帮助工程师高效完成土方平衡分析,提升工程预算精度。特别适用于河道整治、湿地修复等工程项目,大幅减少人工计算误差和时间成本。

weixin_28698059的博客 221

计算pi小数点后10000位

数据结构课上,老师让我们计算一下pi的10000位,复习一下面向对象。 First Try:定义一个高精度类hp(high precision),实现加减乘除,输入输出,然后随便找一个公式,比如Wallis公式,全都用hp进行计算。 现实是:大数乘大数或者大数除大数,实在是太慢了。而且Wallis公式收敛太慢。 Second Try先找到一个收敛较快的公司:BBP公式,观察公式发现,...

我的技术人生 7394

java高并发系列 - 第22天:java中底层工具类Unsafe,高手必须要了解

java高并发系列 - 第22天:java中底层工具类Unsafe,高手必须要了解 这是java高并发系列第22篇文章,文章基于jdk1.8环境。 本文主要内容 基本介绍、 通过反射获取Unsafe实例 Unsafe中的CAS操作 Unsafe中原子操作相关方法介绍 Unsafe中线程调度相关方法 park和unpark示例 Unsafe锁示例 Unsafe中保证...

weixin_30885111的博客 113

嵌入式开发也玩“全家桶”?手把手教你用CLion+PyOCD调试APM32,比OpenOCD更简单

本文详细介绍了如何使用CLion和PyOCD组合进行APM32嵌入式开发,提供从环境配置到实战调试的完整指南。相比传统工具如Keil和OpenOCD,这套方案更轻量、易用,特别适合现代开发者需求,涵盖CMake配置、PyOCD调试技巧及常见问题解决。

weixin_42543046的博客 463

Java高并发专题之22、java中的UnSafe类

本文主要内容 基本介绍、 通过反射获取Unsafe实例 Unsafe中的CAS操作 Unsafe中原子操作相关方法介绍 Unsafe中线程调度相关方法 park和unpark示例 Unsafe锁示例 Unsafe中保证变量的可见性 Unsafe中Class相关方法 示例:staticFieldOffset、staticFieldBase、staticFieldBase 示例:shouldB...

优快云_KONGlX的博客 234

深入C++学习还必须掌握的一基础知识精讲

施磊C++语言基础提高课程笔记,包括了new/delete;malloc/free;引用和指针;const,指计和引用的结合;函数重载,inline的数,参数带默认值的函数;函数模板等知识点

GeniusAng的博客 1477

Abaqus数据导出技巧:轻松处理大量XY数据

通过上述三种方法,我们可以解决Abaqus中大量XY数据导出的问题。无论是使用插件、编写Python脚本,还是利用Abaqus的内置报告功能,都可以帮助我们高效地处理数据。希望这篇博客能够帮助到你,提高你在Abaqus使用过程中的数据处理效率。

m0_62153576的博客 1915

java之juc

juc是java.util.current的简写,意思是并发编程。

梦想是很难很难的,所以先勇敢一点 603

26、多级快速多极子算法(MLFMA)在大规模电磁问题中的应用

本文探讨了多级快速多极子算法(MLFMA)在大规模电磁问题中的高效应用。通过分层分区策略,MLFMA在分布式内存架构上实现了高效并行化,能够求解包含数亿未知数的复杂电磁问题。文章分析了介质球体、半球透镜、周期性结构和损耗介质阵列等典型案例,验证了MLFMA的高精度与广泛适用性。同时,MLFMA被应用于实际场景如隐身目标Flamme的外部共振分析和含分裂环谐振器的超材料设计,展示了其在航空航天、超材料和生物医学等领域的广阔前景。未来发展方向包括算法优化、多物理场耦合和实时计算,进一步拓展其应用潜力。

t8u9v0w1x的博客 117

6. Java JUC源码分析系列笔记-CAS

1.1. Java JUC源码分析系列笔记-JMM模型之先谈硬件2.2. Java JUC源码分析系列笔记-JMM模型之再谈JMM3.3. Java JUC源码分析系列笔记-Synchronized4.4. Java JUC源码分析系列笔记-锁的优化5.5. Java JUC源码分析系列笔记-volatile6.6. Java JUC源码分析系列笔记-CAS7.7. Java JUC源码分析系列笔记-AQS8.8. Java JUC源码分析系列笔记-手写AQS9.

weixin_28928727的博客 81

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Java unsafe malloc,一线大佬深入讨论JDK中的Unsafe类,给出虚拟机具体实现

Unsafe类99%的开发者不使用Unsafe类,也可能从未听说过它,但是有1%的开发者使用Unsafe类,这些1%的开发者通常写一些广泛使用的库,使得99%的开发者被传递性地使用Unsafe类(尽管Unsafe类的意图是仅为JDK内部提供服务)。目前的用户的困境是:不使用Unsafe类会受到诸多限制,其他可选方案很可能是低效的,比如ByteBuffer API。但如果使用Unsafe类,就失去了...

weixin_42372722的博客 240

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

JUC、JVM

什么是juc?JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包。提供了许多在并发编程工程中常用的工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO、轻量级框架、还提供了多线程上下文的Collectiion的实现。什么是进程和线程?一个软件一个进程,一个进程有许多的线程。进程——资源分配最小单位  线程——程序执行最小单位New 新建状态(线程刚被创建,start方法之前的状态)

qq_42012853的博客 2028

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

JAVA中神奇的双刃剑--Unsafe

参考资料: Java魔法类:sun.misc.Unsafe 在openjdk8下看Unsafe源码 Unsafe介绍 在Oracle的Jdk8无法获取到sun.misc包的源码,想看此包的源码可以直接下载openjdk,包的路径是: openjdk-8u40-src-b25-10_feb_2015\openjdk\jdk\src\share\classes\sun\misc。 当然,...

愿我如星君如月 ... 夜夜流光相皎洁 ... 727

JUC:CAS

1. 简介 2. CAS底层原理?谈谈对Unsafe类的理解?   2.1 UnSafe   2.2 源码分析   2.3 底层汇编 3. 原子引用 4. CAS与自旋锁   4.1 简介   4.2 手写自旋锁 5. CAS缺点   5.1 循环时间长开销很大   5.2 ABA问题

2301_79526467的博客 993
上一篇: [java并发]java高并发系列 - 第21天:java中的CAS,你需要知道的东西
下一篇: [java并发] java高并发系列 - 第23天,JUC原子类,一篇就够了
Aran_
博客等级 码龄7年 7粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值