前两天面试被问到“不用锁实现一个多线程并发访问不会造成资源共享问题的方法”
当时我就傻眼了,不用锁怎么做。
想到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); } }
二、我也来封装一个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); } }
三、测试:
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]发现值改变,重新执行...........
看来是好使的。