java Unsafe.java(二)

    上篇取得了Unsafe 实例, 下面分析Unsafe 的方法

1. 阻塞,释放操作

 public native void park(boolean isAbsolute, long time);

调用这个方法的线程阻塞直到下列情况的发生

(1).其他线程调用了unpark(this thread)

(2).被中断

(3).超时

(4). spuriously  (虚假唤醒)

释放线程操作: public native void unpark(Object thread);

2. 操作内存的方法

<span style="white-space:pre">	</span>@Test
	public void testAllocateMemory3() {
		long allocateMemory = unsafe.allocateMemory(4);
		unsafe.putInt(allocateMemory, 0x12345678);
		for (int i = 0; i < 4; i++) {
			byte byte1 = unsafe.getByte(allocateMemory + i);
			String hexString = Integer.toHexString(byte1);
			System.out.println(hexString);
		}
	}
  程序输出如下: 78,  56, 34, 12(具体输出与处理器的大端小端有关), 程序先分配4个字节的内存空间, 然后在内存上写值, 最后按照字节取值。 即使是allocateMemory(1), 也是可以的

 

	@Test
	public void testArray() {
		String[] strArray = new String[10];
		int offset = unsafe.arrayBaseOffset(strArray.getClass());
		int scale = unsafe.arrayIndexScale(strArray.getClass());
		System.out.println(offset);
		System.out.println(scale);

		byte[] bArray = new byte[10];
		bArray[0] = 48;
		int offset2 = unsafe.arrayBaseOffset(bArray.getClass());
		int scale2 = unsafe.arrayIndexScale(bArray.getClass());
		System.out.println(offset2);
		System.out.println(scale2);

		byte b = unsafe.getByte(bArray, 12);
		System.out.println(b);
	}

程序输出如下: 12, 4, 12, 1, 48. arrayBaseOffset 返回在数组对象中,数组元素的起始位置, 在jvm(32位机器) 中每个对象都有一个 “对象头”, 普通对象是8个字节(用来记录对应类在方法区的地址,和加锁情况等), 数组对象是12个字节,其中多出来的4个字节用来记录数组的长度。 而arrayIndexScale() 返回的简单说就是数组元素的字节长度。两个方法中的  getByte (long)  和getByte(Object o, long)  体现了两种寻址方式, 一个是绝对地址, 一个是相对地址寻址,相对object 的地址。 

 public native long staticFieldOffset(Field f);
 public native long objectFieldOffset(Field f);
这两个方法可以得到字段相对地址

3. 类加载相关

<span style="white-space:pre">	</span>public static class ForTest{
		private void f() {
			System.out.println("just for test!!");
		}
	}
	@Test
	public void testDefineClass() throws Exception {
		URL resource = this.getClass().getClassLoader()
				.getResource("com/jdkstudy/juc/MyUnsafe$ForTest.class");
		File classFile = new File(resource.toURI());
		InputStream in = new FileInputStream(classFile);
		byte[] b = new byte[10000];
		int length = in.read(b);
		byte[] right = new byte[length];
		System.arraycopy(b, 0, right, 0, length);
		
		@SuppressWarnings("deprecation")
		Class<?> defineClass = unsafe.defineClass(
				"com.jdkstudy.juc.MyUnsafe$ForTest", b, 0, length);
		ForTest ins = (ForTest) defineClass.newInstance();
		ins.f();
		in.close();
	}

4. CAS 的操作

以public final native boolean compareAndSwapInt(Object o, long offset,   int expected,   int x);为例:

public class TestUnsafeCAS {
	private volatile int numCAS;
	public static void main(String[] args) throws InterruptedException {
		int threadNum = 10000;
		final CountDownLatch cdl1 = new CountDownLatch(threadNum);
		final CountDownLatch cdl2 = new CountDownLatch(1);
		final TestUnsafeCAS instance = new TestUnsafeCAS();

		for (int i = 0; i < threadNum; i++) {
			Thread t = new Thread(new Runnable() {

				@Override
				public void run() {
					try {
						cdl2.await();
						//something()
						//instance.numCAS++; // use ++ without CAS
						for(;;) {
							int expect = instance.numCAS;
							// use CAS to update
							if(unsafe.compareAndSwapInt(instance, numCASOffset, expect, expect + 1)) break;
						}
						cdl1.countDown();
					} catch (Exception e) {
					}
				}
			});
			t.start();
		}
		cdl2.countDown();
		cdl1.await();
		System.out.println(instance.numCAS);
	}

	private static final Unsafe unsafe = MyUnsafe.getUnsafe();
	private final static long numCASOffset;
	static {
		try {
			numCASOffset = unsafe.objectFieldOffset(TestUnsafeCAS.class
					.getDeclaredField("numCAS"));
		} catch (Exception ex) {
			throw new Error(ex);
		}
	}
}
如果直接使用 ++ 肯定会有并发问题, 会出现numCAS <threadNum 。 但是使用CAS 操作numCAS就不会有这个问题(失败了会重试直到成功)。 AtomicInteger已经帮我们实现了这样的功能, 参看AtomicInteger 源码会发现, 其就是用volatile的int变量来保存一个值, 用cas来更新这个值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值