AtomicInteger分析--自实现无锁并发

前两天面试被问到“不用锁实现一个多线程并发访问不会造成资源共享问题的方法”

当时我就傻眼了,不用锁怎么做。

想到android中有一个looper就说了用队列,不过这答案并不是面试官满意的,也对,用队列不也要用到锁吗?


这几天看了编程思想上的共享资源那节,以及好多博客上对volatile与Atomic*类的描述,算是懂了一些了,感觉收获很大。

关于原子性,可视性,有序性这里就不说了,上一篇转载的博客写的很好很详细。

点击打开链接


一、下面就来分析一下AtomicInteger的这个包装类:

1.他有什么功能:

基本上就是简单的赋值与++ --操作

2.怎么实现的:

这里使用到了一个unsafe来控制


1.关于unsafe:

以前我也用过这个类,那时候的用处是反射,听说用了这个类,反射速度能快很多,测试了后发现也的确如此,快了很多很多。

不过这个类的名字Unsafe,还有他操作起来稍微有些复杂,搞的我在反射的时候不怎么敢用他,乖乖的用原先的方法。


这几天看的博客中,记得有一篇中说到Unsafe他是直接操作计算机cpu的,sun并不希望程序员来用他,因为对内存不是足够了解使用起来会造成不安全。

那unsafe应该就是这个意思了:是怕我们使用的时候使用不当而带来不安全。

unsafe怎么使用:

这个类是在sun.misc包下的,正常使用eclipse并看不到这个类。

右键工程->properties->java Build Path->Libraries->点击展开jre system library->access rules->edit->add : access 输入**->完事

但是这个Unsafe还加了保护:

首先是不能new

其次是在AtomicInteger中是用静态方法getUnsafe得到的,这里我们也不能用

反射也不行产生对象也不行

只能通过反射,得到他里面一个叫做theUnsafe的属性,然后获取。


稍微有点复杂,所以我专门写了一个获取渠道:

package com.test.atomic;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class UnsafeFactory {
	private static Unsafe unsafe = null;
	static {
		try {
			Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
			unsafeField.setAccessible(true);
			unsafe = (Unsafe) unsafeField.get(null);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Error("unsafe init error");
		}
	}

	public static Unsafe get() {
		return unsafe;
	}
}



2.我们回到AtomicInteger类中,随便找一个方法:

   public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }
就这个把,一个死循环,然后调用另一个方法,直到返回值为true,否则一直跑。

其他很多方法也一样的格式,也同样调用了compareAndSet方法,那就看看这个方法是干嘛的

  public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
直接调用unsafe的compareAndSwapInt方法,再多的也看不到了

相似的还有compareAndSwapLong/compareAndSwapObject方法


根据这几天看的资料推断,这个方法的意思就是去拿着原先的值和新的值去内存看看(这个操作是原子性的)。

如果我手上的原先的值和内存里面的还是一样的,那就更新,返回true;如果不一样,就不管,返回false。

后面2个int的取名字也去的非常的准确,“我预料他是这样的”,“更新”

还有前面的两个参数,其实就是为了找到这个要赋值的属性的位置:

1.不得找到对象的位置吗,用this

2.然后再找属性相对于对象的偏移,用offset,这个offset也是通过unsafe算出来的:

 private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

如果学习过jni,并且用jni做过反射的同学肯定对偏移不陌生。


二、我也来封装一个Atomic*?

知道了unsafe.compareAndSwap*方法,那么以验证与创新的角度去试试他的强大之处。

其实我是看了AtomicInteger没有带一个本地方法,而且简单。那么我是不是也能写个这个啊?

贴上代码:

package com.test.atomic;

import sun.misc.Unsafe;

public class CustomAtomic {

	private static final Unsafe unsafe = UnsafeFactory.get();
	private volatile int value;
	private static final long valueOffset;

	static {
		try {
			valueOffset = unsafe.objectFieldOffset(CustomAtomic.class
					.getDeclaredField("value"));
		} catch (Exception ex) {
			throw new Error(ex);
		}
	}

	public void complexOperate(CallBack operation) {
		for (;;) {
			int formerValue = value;
			int newValue = operation.action(formerValue);
			if (compareAndSet(formerValue, newValue)) {
				System.out.println("线程" + Thread.currentThread() + "成功赋值;原值:"
						+ formerValue + ",新值:" + newValue + "!!!!!!!!!!!");
				return;
			}
			System.out.println("线程" + Thread.currentThread()
					+ "发现值改变,重新执行...........");
		}
	}

	private boolean compareAndSet(int expect, int update) {
		return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
	}

	static interface CallBack {
		int action(int formerValue);
	}
}

感觉我这个比AtomicIntege强多了,多了一个回调方法,这样功能就不再局限了,想怎么操作就怎么操作。


三、测试:

package com.test.atomic;

import com.test.atomic.CustomAtomic.CallBack;

public class MyAtomicTest {
	public static void main(String[] args) {
		CustomAtomic a = new CustomAtomic();
		T1 b = new T1(a);
		T1 c = new T1(a);
		T1 d = new T1(a);
		T1 e = new T1(a);
		b.start();
		c.start();
		d.start();
		e.start();
	}
}

class T1 extends Thread {
	CustomAtomic a;

	T1(CustomAtomic a) {
		this.a = a;
	}

	@Override
	public void run() {
		CustomAtomic.CallBack callback = new CallBack() {

			public int action(int formerValue) {
				long start = System.currentTimeMillis();
				int update = 0;
				try {
					System.out.println("线程" + Thread.currentThread()
							+ "开始执行,值为:" + formerValue);
					Thread.sleep(500);
					return update = formerValue + 1;
				} catch (InterruptedException e) {
					e.printStackTrace();
					throw new RuntimeException("!!");
				} finally {
					System.out.println("线程" + Thread.currentThread()
							+ "执行完毕,耗时" + (System.currentTimeMillis() - start)
							+ ",值为:" + update);
				}
			}
		};
		while (true) {
			a.complexOperate(callback);
		}
	}
}

就贴一小段结果:

线程Thread[Thread-2,5,main]开始执行,值为:0
线程Thread[Thread-3,5,main]开始执行,值为:0
线程Thread[Thread-0,5,main]开始执行,值为:0
线程Thread[Thread-1,5,main]开始执行,值为:0
线程Thread[Thread-2,5,main]执行完毕,耗时515,值为:1
线程Thread[Thread-2,5,main]成功赋值;原值:0,新值:1!!!!!!!!!!!
线程Thread[Thread-2,5,main]开始执行,值为:1
线程Thread[Thread-3,5,main]执行完毕,耗时515,值为:1
线程Thread[Thread-3,5,main]发现值改变,重新执行...........
线程Thread[Thread-3,5,main]开始执行,值为:1
线程Thread[Thread-0,5,main]执行完毕,耗时515,值为:1
线程Thread[Thread-0,5,main]发现值改变,重新执行...........
线程Thread[Thread-0,5,main]开始执行,值为:1
线程Thread[Thread-1,5,main]执行完毕,耗时515,值为:1
线程Thread[Thread-1,5,main]发现值改变,重新执行...........
线程Thread[Thread-1,5,main]开始执行,值为:1
线程Thread[Thread-3,5,main]执行完毕,耗时500,值为:2
线程Thread[Thread-3,5,main]成功赋值;原值:1,新值:2!!!!!!!!!!!
线程Thread[Thread-3,5,main]开始执行,值为:2
线程Thread[Thread-1,5,main]执行完毕,耗时500,值为:2
线程Thread[Thread-1,5,main]发现值改变,重新执行...........
线程Thread[Thread-1,5,main]开始执行,值为:2
线程Thread[Thread-2,5,main]执行完毕,耗时500,值为:2
线程Thread[Thread-2,5,main]发现值改变,重新执行...........
线程Thread[Thread-2,5,main]开始执行,值为:2
线程Thread[Thread-0,5,main]执行完毕,耗时500,值为:2
线程Thread[Thread-0,5,main]发现值改变,重新执行...........
线程Thread[Thread-0,5,main]开始执行,值为:2
线程Thread[Thread-3,5,main]执行完毕,耗时502,值为:3
线程Thread[Thread-3,5,main]成功赋值;原值:2,新值:3!!!!!!!!!!!
线程Thread[Thread-3,5,main]开始执行,值为:3
线程Thread[Thread-0,5,main]执行完毕,耗时502,值为:3
线程Thread[Thread-0,5,main]发现值改变,重新执行...........
线程Thread[Thread-0,5,main]开始执行,值为:3
线程Thread[Thread-1,5,main]执行完毕,耗时505,值为:3
线程Thread[Thread-1,5,main]发现值改变,重新执行...........
线程Thread[Thread-1,5,main]开始执行,值为:3
线程Thread[Thread-2,5,main]执行完毕,耗时505,值为:3
线程Thread[Thread-2,5,main]发现值改变,重新执行...........
线程Thread[Thread-2,5,main]开始执行,值为:3
线程Thread[Thread-3,5,main]执行完毕,耗时502,值为:4
线程Thread[Thread-3,5,main]成功赋值;原值:3,新值:4!!!!!!!!!!!
线程Thread[Thread-3,5,main]开始执行,值为:4
线程Thread[Thread-0,5,main]执行完毕,耗时501,值为:4
线程Thread[Thread-0,5,main]发现值改变,重新执行...........
线程Thread[Thread-0,5,main]开始执行,值为:4
线程Thread[Thread-1,5,main]执行完毕,耗时514,值为:4
线程Thread[Thread-1,5,main]发现值改变,重新执行...........
线程Thread[Thread-1,5,main]开始执行,值为:4
线程Thread[Thread-2,5,main]执行完毕,耗时514,值为:4
线程Thread[Thread-2,5,main]发现值改变,重新执行...........
线程Thread[Thread-2,5,main]开始执行,值为:4
线程Thread[Thread-0,5,main]执行完毕,耗时501,值为:5
线程Thread[Thread-0,5,main]成功赋值;原值:4,新值:5!!!!!!!!!!!
线程Thread[Thread-0,5,main]开始执行,值为:5
线程Thread[Thread-3,5,main]执行完毕,耗时501,值为:5
线程Thread[Thread-3,5,main]发现值改变,重新执行...........


看来是好使的。



你可能感兴趣的:(javase,java性能优化)