首先是一種鎖,與互斥鎖相似,基本作用是用於線程(進程)之間的同步。與普通鎖不同的是,一個線程A在獲得普通鎖後,如果再有線程B試圖獲取鎖,那麼這個線程B將會掛起(阻塞);試想下,如果兩個線程資源競爭不是特別激烈,而處理器阻塞一個線程引起的線程上下文的切換的代價高於等待資源的代價的時候(鎖的已保持者保持鎖時間比較短),那麼線程B可以不放棄CPU時間片,而是在“原地”忙等,直到鎖的持有者釋放了該鎖,這就是自旋鎖的原理,可見自旋鎖是一種非阻塞鎖。
二、自旋鎖可能引起的問題:
1.過多佔據CPU時間:如果鎖的當前持有者長時間不釋放該鎖,那麼等待者將長時間的佔據cpu時間片,導致CPU資源的浪費,因此可以設定一個時間,當鎖持有者超過這個時間不釋放鎖時,等待者會放棄CPU時間片阻塞;
2.死鎖問題:試想一下,有一個線程連續兩次試圖獲得自旋鎖(比如在遞歸程序中),第一次這個線程獲得了該鎖,當第二次試圖加鎖的時候,檢測到鎖已被佔用(其實是被自己佔用),那麼這時,線程會一直等待自己釋放該鎖,而不能繼續執行,這樣就引起了死鎖。因此遞歸程序使用自旋鎖應該遵循以下原則:遞歸程序決不能在持有自旋鎖時調用它自己,也決不能在遞歸調用時試圖獲得相同的自旋鎖。
三、JAVA中一種自旋鎖的實現:
<span style="font-size:14px;">import java.util.concurrent.atomic.AtomicReference;
class SpinLock {
//java中原子(CAS)操作
AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋鎖的線程對象
private int count;
public void lock() {
Thread cur = Thread.currentThread();
//lock函數將owner設置爲當前線程,並且預測原來的值爲空。unlock函數將owner設置爲null,並且預測值爲當前線程。當有第二個線程調用lock操作時由於owner值不爲空,導致循環
//一直被執行,直至第一個線程調用unlock函數將owner設置爲null,第二個線程才能進入臨界區。
while (!owner.compareAndSet(null, cur)){
}
}
public void unLock() {
Thread cur = Thread.currentThread();
owner.compareAndSet(cur, null);
}
}
}
public class Test implements Runnable {
static int sum;
private SpinLock lock;
public Test(SpinLock lock) {
this.lock = lock;
}
public static void main(String[] args) throws InterruptedException {
SpinLock lock = new SpinLock();
for (int i = 0; i < 100; i++) {
Test test = new Test(lock);
Thread t = new Thread(test);
t.start();
}
Thread.currentThread().sleep(1000);
System.out.println(sum);
}
@Override
public void run() {
this.lock.lock();
sum++;
this.lock.unLock();
}
}</span>
四、java CAS
CAS是一種系統原語,是Compare And Set的縮寫。