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