1. 程式人生 > >java 自旋鎖(可重入且無死鎖)

java 自旋鎖(可重入且無死鎖)

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()迴圈裡,後果不敢想了。