前言
为了研究AQS,我们先来学习下java中cas(Compare And Swap)的基础Unsafe类的使用
概念
Unsafe产生于java无法向c那样操作底层操作系统,但一些场景又需要相关操作.所以此类提供了一些java语言对于操作系统内存层面操作的API.这显然被认为是不安全的,所以此类是不公开的,不建议被java应用直接使用.
但现实中已经有大量的java并发相关操作的框架在使用它了....据说此类在计划废弃中.
使用
Unsafe能操作内存?这个是什么概念?都有哪些操作呢?
其实最明显的是它大量方法都是直接操作内存地址进行操作的.方法可以分为下面几类:
- 自己引用测试类:
因Unsafe方法的不安全性(比如可以直接操作内存,但jvm无法管理,造成oom),所以我们只能通过反射的方法进行使用:
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
- 线程操作类:
我们可以使用LockSupport类进行操作
a. LockSupport.park()对应Unsafe的Unsafe.park(false, 0L)------>给当前所在线程加锁,第一个参数表示true为精度型单位为纳秒,false单位毫秒,第二次参数表示等待时间;
b. LockSupport.park.unpark --------->Thread thread对应Unsafe的UNSAFE.unpark(thread)方法(解锁指定线程)
如果,我们直接使用Unsafe,是这样子的:
public static void main(String[] args)
throws IllegalAccessException, NoSuchFieldException, IOException, InterruptedException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
Thread thread=new Thread(
()->{
unsafe.park(false,0l);
System.out.println("线程一执行");
}
);
thread.start();
Thread thread2=new Thread(
()->{
unsafe.park(false,0l);
System.out.println("线程二执行");
}
);
thread2.start();
TimeUnit.SECONDS.sleep(1l);
unsafe.unpark(thread2);
unsafe.unpark(thread);
System.in.read();
}
- 对象属性操作类:
我们还可以通过Unsafe类获取对象的属性值.因为Unsafe类是直接操作内存的,所以需要我们获得对应的属性内存地址,如下操作:
private static int ASHIFT;
private static long ABASE;
public static void main(String[] args)
throws IllegalAccessException, NoSuchFieldException, IOException, InterruptedException {
//1。获取unsafe对象
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
//2.拒两个我们测试用的数据对象
String[] array1 = new String[]{"fd", "sds", "hffij", "er", "er", "sds"};
String[] array2 = new String[]{"ff", "fd", "fdf", "xcv", "xcv", "cv"};
//3。获取String[].class类的内存地址
Class<?> ak = String[].class;
ABASE = unsafe.arrayBaseOffset(ak);
//4.获取此数组的每个成员的内存偏移量(就是每个对象占内存大小)
int scale = unsafe.arrayIndexScale(ak);
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
//5。获取对应位置的属性,如2 << ASHIFT,表示位置为2的数据(2*偏移量)
String array11 = (String) unsafe.getObject(array1, ((long) 2 << ASHIFT) + ABASE);
String array21 = (String) unsafe.getObject(array2, ((long) 5 << ASHIFT) + ABASE);
System.out.println(ABASE);
System.out.println(scale);
System.out.println(ASHIFT);
System.out.println(array11);
System.out.println(array21);
}
- cas原子操作类:
如下操作,通过unsafe类实现cas原子操作.
static Unsafe UNSAFE = null;
private Object test1;
public Unsafe3 setTest1(Object test1) {
this.test1 = test1;
return this;
}
private static long test1Index;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
UNSAFE = (Unsafe) f.get(null);
//获取Unsafe3对象内存位置
Class u = Unsafe3.class;
//获取此对象中test1属性内存偏移
test1Index = UNSAFE.objectFieldOffset
(u.getDeclaredField("test1"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Object o = new Object();
Unsafe3 unsafe3 = new Unsafe3();
//test1属性赋值
unsafe3.setTest1(o);
Object o2 = new Object();
//cas操作,匹配原有属性是否为o,是则赋新值,返回ture
boolean b = UNSAFE.compareAndSwapObject(unsafe3, test1Index, o2, o);
System.out.println(b);
boolean b2 = UNSAFE.compareAndSwapObject(unsafe3, test1Index, o, o2);
System.out.println(b2);
}
总结
好了,上面就是unsafe的基本几种使用,其也是aqs框架中cas操作的基础.下面我们进行aqs相关学习.
AQS研究系列(二)--线程状态和interrupt()、interrupted()、isInterrupted等方法学习
AQS研究系列(三)--AbstractQueuedSynchronizer源码分析