java 并发包 co_【java基础】并发包concurrent概述

concurrent(并发)

在java中,面试最高频的一个问题,就是并发.那么你对并发有多少了解呢?本系列会详细介绍并发包中的重点类包,高频使用类,高频面试问题.

结构

package java.util.concurrent;atomic:原子包,基础数据类型的原子操作类型包.对基础类型进行了封装,用于基础类型并发下的原子计算.

Lock:锁包,多线程处理相同数据时,使用锁来保证数据库的准确性的最常用的方式,本包下是锁相关类.

其他类:jdk原生中吧其他类合并存放,未区分类包.

atomic包

我们以AtomicInteger举例.

在使用atomic包进计算的时候普遍使用的几个方法:addAndGet(int delta)先加再获取,拿到是加以后的数据

getAndAdd(int delta)先获取再加,拿到的是加以前的数据

我们用addAndGet方法举例,查看实现,源码如下:

public final int addAndGet(int delta) {

return unsafe.getAndAddInt(this, valueOffset, delta) + delta;

}`

在这里我们看到了一个神奇的类unsafe.现在先来看下getAndAddInt这个方法.我们可以看到这个方法的具体实现如下:

public final int getAndAddInt(Object var1, long var2, int var4) {

int var5;

do {

var5 = this.getIntVolatile(var1, var2);

} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;

}

compareAndSwapInt是不是很熟悉,aotmic也是使用CAS算法去实现的.

CAS算法详解

下面我们来详细看下unsafe这个类

Unsafe类介绍

第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe...读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能。

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

我们先来看看Unsafe的初始化方法,这是一个单例模式:

private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();

public static Unsafe getUnsafe() {

Class cc = sun.reflect.Reflection.getCallerClass(2);

if (cc.getClassLoader() != null)

throw new SecurityException("Unsafe");

return theUnsafe;

}

方法中,限制了它的 ClassLoader,如果这个方法的调用实例不是由Boot ClassLoader加载的,则会报错。可以做一个实验,因为Java源码中的类,除扩展包都是由Boot ClassLoader加载的,如果我们new一个Object对象,查看Object对象的ClassLoader,它一定是null。所以,正常情况下开发者无法直接使用Unsafe ,如果需要使用它,则需要利用反射:

private static Unsafe getUnsafe(){

try {

Field field = Unsafe.class.getDeclaredField("theUnsafe");

field.setAccessible(true);

Unsafe unsafe = (Unsafe) field.get(null);

return unsafe;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

方法详解

操作属性方法public native Object getObject(Object o, long offset); 通过给定的Java变量获取引用值。这里实际上是获取一个Java对象o中,获取偏移地址为offset的属性的值,此方法可以突破修饰符的抑制,也就是无视private、protected和default修饰符。类似的方法有getInt、getDouble等等。同理还有putObject方法。

public native Object getObjectVolatile(Object o, long offset);强制从主存中获取属性值。类似的方法有getIntVolatile、getDoubleVolatile等等。同理还有putObjectVolatile。

public native void putOrderedObject(Object o, long offset, Object x);设置o对象中offset偏移地址offset对应的Object型field的值为指定值x。这是一个有序或者有延迟的putObjectVolatile方法,并且不保证值的改变被其他线程立即看到。只有在field被volatile修饰并且期望被修改的时候使用才会生效。类似的方法有putOrderedInt和putOrderedLong。

public native long staticFieldOffset(Field f);返回给定的静态属性在它的类的存储分配中的位置(偏移地址)。

public native long objectFieldOffset(Field f);返回给定的非静态属性在它的类的存储分配中的位置(偏移地址)。

public native Object staticFieldBase(Field f);返回给定的静态属性的位置,配合staticFieldOffset方法使用。

操作数组public native int arrayBaseOffset(Class arrayClass);返回数组类型的第一个元素的偏移地址(基础偏移地址)。

public native int arrayIndexScale(Class arrayClass);返回数组中元素与元素之间的偏移地址的增量。

这两个方法配合使用就可以定位到任何一个元素的地址。

内存管理public native int addressSize();获取本地指针的大小(单位是byte),通常值为4或者8。常量ADDRESS_SIZE就是调用此方法。

public native int pageSize();获取本地内存的页数,此值为2的幂次方。

public native long allocateMemory(long bytes);分配一块新的本地内存,通过bytes指定内存块的大小(单位是byte),返回新开辟的内存的地址。

public native long reallocateMemory(long address, long bytes);通过指定的内存地址address重新调整本地内存块的大小,调整后的内存块大小通过bytes指定(单位为byte)。

public native void setMemory(Object o, long offset, long bytes, byte value);将给定内存块中的所有字节设置为固定值(通常是0)。

线程挂起和恢复public native void unpark(Object thread);释放被park创建的在一个线程上的阻塞。由于其不安全性,因此必须保证线程是存活的。

public native void park(boolean isAbsolute, long time);阻塞当前线程,一直等道unpark方法被调用。

6、内存屏障

(1)public native void loadFence();

在该方法之前的所有读操作,一定在load屏障之前执行完成。

(2)public native void storeFence();

在该方法之前的所有写操作,一定在store屏障之前执行完成public native void fullFence();在该方法之前的所有读写操作,一定在full屏障之前执行完成,这个内存屏障相当于上面两个(load屏障和store屏障)的合体功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值