推薦:Java并發程式設計彙總
Java并發程式設計一引用類型、更新類型原子類初使用加源碼分析
首先我們來看一看有哪些原子類。
現在我們來看看該如何去使用這些引用類型、更新類型原子類吧。
之前已經介紹過基本類型、數組類型原子類和累加器的使用了,講過的原理這裡就不會再涉及了,想了解就看下面這篇部落格吧。
Java并發程式設計一基本類型、數組類型原子類和累加器初使用加源碼分析
AtomicReference
AtomicReference
類會将一個引用類型包裝成原子類,現在我們來看一看如何使用吧。
這裡我們使用
AtomicReference
類來實作一個簡易版自旋鎖。
代碼:
package lock.spinlock;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
private AtomicReference<Thread> sign = new AtomicReference<>();
public void lock() {
Thread current = Thread.currentThread();
while (!sign.compareAndSet(null, current)) {
System.out.println("自旋擷取失敗,再次嘗試");
}
}
public void unlock() {
Thread current = Thread.currentThread();
sign.compareAndSet(current, null);
}
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "開始嘗試擷取自旋鎖");
spinLock.lock();
System.out.println(Thread.currentThread().getName() + "擷取到了自旋鎖");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "釋放了自旋鎖");
spinLock.unlock();
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
開頭輸出:
Thread-1開始嘗試擷取自旋鎖
Thread-0開始嘗試擷取自旋鎖
Thread-1擷取到了自旋鎖
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
結尾輸出:
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
Thread-0釋放了自旋鎖
自旋擷取失敗,再次嘗試
Thread-1擷取到了自旋鎖
Thread-1釋放了自旋鎖
中間輸出還有很多的
自旋擷取失敗,再次嘗試
。
進入
AtomicReference
類源碼,可以看到
AtomicReference
類将引用類型包裝成原子類型的關鍵-
Unsafe
工具類,
Unsafe
工具類在之前的那篇部落格已經介紹過了,這裡就不再贅述。
private static final Unsafe unsafe = Unsafe.getUnsafe();
同樣使用反射來擷取要操作的屬性。
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
使用
volatile
關鍵字修飾
value
屬性,保證了
value
屬性的可見性,這和之前講過的原子類是一樣的。
private volatile V value;
我們來看看
AtomicReference
類有哪些常用的方法。
/**
* Gets the current value.
*
* @return the current value
*/
public final V get() {
return value;
}
get()
會擷取目前的
value
。
/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
}
set(V newValue)
會進行指派操作,指派操作是原子操作,又因為用
volatile
關鍵字修飾了
value
屬性,保證了
value
屬性的可見性,這次指派操作,對其他線程是可見的。
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
@SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
}
getAndSet(V newValue)
會先擷取舊值再賦新值,和之前介紹過的基本類型原子類一樣,這些方法都是原子操作,不然就會有線程安全的問題了。
就介紹這些常用的方法吧,其他的方法大家可以自己去看看源碼。
AtomicIntegerFieldUpdater
AtomicIntegerFieldUpdater
類,根據類名我們可能就已經知道它的作用了,它是将一個引用類型的
int(自動裝箱即可)或者Integer
類型的屬性更新為與原子類型。
我們來看一看它的用法吧。
代碼:
package atomic;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}
public static class Candidate {
volatile int score;
}
public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通變量:"+peter.score);
System.out.println("更新後的結果"+ tom.score);
}
}
輸出:
普通變量:18860
更新後的結果20000
很明顯,普通變量的
++
操作存線上程安全問題,而經過使用
AtomicIntegerFieldUpdater
類進行更新的變量就沒有線程安全問題了(使用正确的情況下)。
從這段代碼可以很容易看出來,這裡也使用了反射。
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
/**
* Creates and returns an updater for objects with the given field.
* The Class argument is needed to check that reflective types and
* generic types match.
*
* @param tclass the class of the objects holding the field
* @param fieldName the name of the field to be updated
* @param <U> the type of instances of tclass
* @return the updater
* @throws IllegalArgumentException if the field is not a
* volatile integer type
* @throws RuntimeException with a nested reflection-based
* exception if the class does not hold field or is the wrong type,
* or the field is inaccessible to the caller according to Java language
* access control
*/
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
使用
AtomicIntegerFieldUpdater
類進行更新的變量其實就具有了和
AtomicInteger
類一樣的功能。
部分源碼:
public abstract int get(T obj);
public int getAndSet(T obj, int newValue) {
int prev;
do {
prev = get(obj);
} while (!compareAndSet(obj, prev, newValue));
return prev;
}
public int getAndIncrement(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev + 1;
} while (!compareAndSet(obj, prev, next));
return prev;
}
public int getAndDecrement(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev - 1;
} while (!compareAndSet(obj, prev, next));
return prev;
}
public int getAndAdd(T obj, int delta) {
int prev, next;
do {
prev = get(obj);
next = prev + delta;
} while (!compareAndSet(obj, prev, next));
return prev;
}
public int incrementAndGet(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev + 1;
} while (!compareAndSet(obj, prev, next));
return next;
}
public int decrementAndGet(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev - 1;
} while (!compareAndSet(obj, prev, next));
return next;
}
public int addAndGet(T obj, int delta) {
int prev, next;
do {
prev = get(obj);
next = prev + delta;
} while (!compareAndSet(obj, prev, next));
return next;
}