最近感兴趣的内容到J.U.C包中了,当然了,对于这个包我的理解还是只看见大门,首先,自己没机会在项目中实战,其次,平时很少练手,再其次,最近才看,所以很多文章都是网上大牛写的,自己翻译过来,就当做逼自己重新看一遍了。
首先谈谈自己对于锁的恐惧吧,其实对于每个初级人员来说,锁是一个高深的问题,一般情况下我估计所有的初级人员来说的话,都很少使用,即使使用了,也是简单的synchronized吧,对于其他的锁我估计都很少使用,笔者最近使用了redis锁,说来其实很讽刺啊,单服务器的锁还没玩的溜,都开始装逼使用redis锁了,hh。
我对于使用synchronized的理解:
1) 当前线程持有锁的话,其他线程就挂起了。
2) 如果在有竞争的情况下呢?加锁和释放锁肯定会带来性能上的消耗吧。
3) 如果一个线程的优先级高,但是确在等待一个优先级低的线程释放锁,是否会有问题(这个只是猜测了,本人其实不知道),纯粹来源于网络知识。
两种锁:悲观锁和乐观锁。
synchronized就是一个悲观锁,会导致所有的线程等待当前线程释放锁之后才能进入。
乐观锁就是每次不加锁而是假设没有冲突去完成某项操作,如果因为冲突失败了就重试,直至成功,乐观锁的一种实现就是CAS Compare And Swap.
CAS有三个操作数----内存旧值(V),预期原值(A) 和新值(B)。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
CAS利用了CPU的CAS指令和 JNI的来完成java的非阻塞算法。
关于ABA问题不去记录了,现在已近不存在这个问题了。
具体来看AtomicInteger这个类。Unsafe这个类就是java代码和计算机底层进行交互时使用的代码。这个我不想关注,知道即可。看其中的valueOffset是什么?CAS是拿期望的值和原本的值作比较,相同的话就更新为新值,那这个valueOffset其实就是原本的值。这个方法拿到的就是一个原本的值的内存地址(为什么要看内存地址,就是去解决ABA问题的)。
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
value是volatile的,volatile变量在内存中对任意线程都是可见的,但是它不能保证原子性
private volatile int value;
来看看这个方法。内部是个无线循环,这就是上文提到的乐观锁,假设没有竞争,每个程序都是在无线循环,直至条件满足,return掉。
current和next这两个操作都是非线程安全的,关键的就在于compareAndSet这个方法。
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
这边对于get方法来说只传入了current和next的值,但是底层计算时并不是这样,this,当前对象的地址,valueOffset的为原本值的相对对象的偏移量,expect为期望的值,update为新值,也就是expect的值和valueOffset处的值相等时,更新为新值。
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
其他方法多多少少都是基于此,就不一一看了。
但是AtomicInteger并不能来测试啊,看不见输出啊, 看不见每个线程的旧值,新值啊。怎么办呢?自己实现一个呗。
首先要结局unsafe这个玩意儿吧,至于怎么获取,www.baidu.com,可以找到吧,好像只提供了一个获取方式,反射获取。
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;
}
}
自己实现的,参考AtomicInteger就行了,没什么难处
public class UserAtomicInteger {
private static final Unsafe unsafe = UnsafeFactory.get();
private volatile int value;
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset(UserAtomicInteger.class
.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}
public UserAtomicInteger(int initialValue) {
value = initialValue;
}
public void compareAndSwap(){
for(;;){
try{
System.out.println(Thread.currentThread()+"当前值为: "+value);
int oldValue = value;
int newValue = oldValue + 1;
if(compareAndSet(oldValue,newValue)){
System.out.println("线程: "+ Thread.currentThread() + "赋值成功。value: "+ value+ " old : "+ oldValue + " new : "+ newValue);
}
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
private boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
测试类,起4个线程,while一直去运行。
public class TestAtomic {
public static void main(String[] args) {
UserAtomicInteger a = new UserAtomicInteger(1);
MyThread t1 = new MyThread(a);
MyThread t2 = new MyThread(a);
MyThread t3 = new MyThread(a);
MyThread t4 = new MyThread(a);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyThread extends Thread{
UserAtomicInteger use;
public MyThread(UserAtomicInteger use){
this.use = use;
}
@Override
public void run() {
try{
while (true){
use.compareAndSwap();
}
}catch (Exception e){
e.getMessage();
}
}
}
可以看见输出,
看见第5行和第7行,线程1读取到为3,线程3读取到也为3,但是线程1进入了CAS过程,然后值为4了,线程3虽然读到了是3,但是此时值已变,最新值是4
Thread[Thread-0,5,main]当前值为: 1
线程: Thread[Thread-0,5,main]赋值成功。value: 2 old : 1 new : 2
Thread[Thread-2,5,main]当前值为: 2
线程: Thread[Thread-2,5,main]赋值成功。value: 3 old : 2 new : 3
Thread[Thread-1,5,main]当前值为: 3
线程: Thread[Thread-1,5,main]赋值成功。value: 4 old : 3 new : 4
Thread[Thread-3,5,main]当前值为: 3
线程: Thread[Thread-3,5,main]赋值成功。value: 5 old : 4 new : 5
Thread[Thread-0,5,main]当前值为: 5
线程: Thread[Thread-0,5,main]赋值成功。value: 6 old : 5 new : 6
Thread[Thread-2,5,main]当前值为: 6
线程: Thread[Thread-2,5,main]赋值成功。value: 7 old : 6 new : 7
Thread[Thread-3,5,main]当前值为: 7
线程: Thread[Thread-3,5,main]赋值成功。value: 8 old : 7 new : 8
Thread[Thread-1,5,main]当前值为: 7
线程: Thread[Thread-1,5,main]赋值成功。value: 9 old : 8 new : 9
Thread[Thread-0,5,main]当前值为: 9
线程: Thread[Thread-0,5,main]赋值成功。value: 10 old : 9 new : 10
Thread[Thread-2,5,main]当前值为: 10
线程: Thread[Thread-2,5,main]赋值成功。value: 11 old : 10 new : 11
Thread[Thread-1,5,main]当前值为: 11
线程: Thread[Thread-1,5,main]赋值成功。value: 12 old : 11 new : 12
Thread[Thread-3,5,main]当前值为: 11
线程: Thread[Thread-3,5,main]赋值成功。value: 13 old : 12 new : 13
Thread[Thread-0,5,main]当前值为: 13
线程: Thread[Thread-0,5,main]赋值成功。value: 14 old : 13 new : 14
Thread[Thread-2,5,main]当前值为: 14
线程: Thread[Thread-2,5,main]赋值成功。value: 15 old : 14 new : 15
Thread[Thread-3,5,main]当前值为: 15
线程: Thread[Thread-3,5,main]赋值成功。value: 16 old : 15 new : 16
Thread[Thread-1,5,main]当前值为: 16
线程: Thread[Thread-1,5,main]赋值成功。value: 17 old : 16 new : 17
Thread[Thread-0,5,main]当前值为: 17
线程: Thread[Thread-0,5,main]赋值成功。value: 18 old : 17 new : 18
Thread[Thread-2,5,main]当前值为: 18
线程: Thread[Thread-2,5,main]赋值成功。value: 19 old : 18 new : 19
Thread[Thread-1,5,main]当前值为: 19
线程: Thread[Thread-1,5,main]赋值成功。value: 20 old : 19 new : 20
Thread[Thread-3,5,main]当前值为: 20
线程: Thread[Thread-3,5,main]赋值成功。value: 21 old : 20 new : 21