java 自旋鎖(可重入且無死鎖)
阿新 • • 發佈:2019-02-10
java自旋鎖 的實現原理:如果自旋鎖被另外一個執行緒物件持有,那麼當前獲取鎖的執行緒將陷入while迴圈等待,直到那個持有自旋鎖的執行緒物件釋放它所持有的自旋鎖,那麼那些想要獲取該自旋鎖的執行緒物件 將會有一個獲得該自旋鎖。
基於他這種原理,等待的時候,並不釋放cpu時間片,相比synchronized wait()操作,減小了釋放,重新獲取的消耗。 該自旋鎖適用於,當前執行緒競爭不強烈的時候使用。
程式碼:
public class Test implements Runnable { static int sum; private SpinLock lock; public Test(SpinLock lock) { this.lock = lock; } /** * @param args * @throws InterruptedException */ 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(); this.lock.lock(); sum++; this.lock.unLock(); this.lock.unLock(); } }
<pre name="code" class="java">package optimiz_spinlock; import java.util.concurrent.atomic.AtomicReference; /** * 可重入的自旋鎖 (重新進入 不會出現死鎖) * @author Administrator * */ public class SpinLock { AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋鎖的執行緒物件 private int count;//用一個計數器 來做 重入鎖獲取次數的計數 public void lock() { Thread cur = Thread.currentThread(); if (cur == owner.get()) { count++; return; } while (!owner.compareAndSet(null, cur)) {//當執行緒越來越多 由於while迴圈 會浪費CPU時間片,CompareAndSet 需要多次對同一記憶體進行訪問 //會造成記憶體的競爭,然而對於X86,會採取競爭記憶體匯流排的方式來訪問記憶體,所以會造成記憶體訪問速度下降(其他執行緒老訪問快取),因而會影響整個系統的效能 } } public void unLock() { Thread cur = Thread.currentThread(); if (cur == owner.get()) { if (count > 0) { count--; } else { owner.compareAndSet(cur, null); } } } }
為何說它是可重入的?我們接下來看一個不可重入的自旋鎖(不可重入的自旋鎖,如果再次去獲取鎖的話 很有可能出現死鎖)。
不可重入的自旋鎖的實現:
package optimiz_spinlock; import java.util.concurrent.atomic.AtomicReference; public class BadSpinLock { AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋鎖的執行緒物件 public void lock() { Thread cur = Thread.currentThread(); while (!owner.compareAndSet(null, cur)) { System.out.println(cur.getName()+ " wait lock release"); } } public void unLock() { Thread cur = Thread.currentThread(); if (cur == owner.get()) { owner.compareAndSet(cur, null); System.out.println(cur.getName()+ " release lock"); } } }
package optimiz_spinlock;
public class BadTestTask implements Runnable{
static int sum;
private BadSpinLock lock;
public BadTestTask(BadSpinLock lock) {
this.lock = lock;
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
BadSpinLock lock = new BadSpinLock();
for (int i = 0; i < 2; i++) {
BadTestTask test = new BadTestTask(lock);
Thread t = new Thread(test);
t.start();
}
}
@Override
public void run() {
this.lock.lock();
this.lock.lock();
sum++;
this.lock.unLock();
this.lock.unLock();
}
}
當一個執行緒 呼叫這個不可重入的自旋鎖去加鎖的時候沒問題,當再次呼叫lock()的時候,因為自旋鎖的持有引用已經不為空了,該執行緒物件會誤認為是別人的執行緒持有了自旋鎖,我等我等!,等到地老天荒,哎呦媽啊,原來是自己拿著了,所以這是非常傻逼的問題。 出現這種問題的時候,持有自旋鎖的那個執行緒不會釋放該自旋鎖,導致所有執行緒都會停留在while()迴圈裡,後果不敢想了。