java併發包中的Lock

1)Lock與synchronized的區別:java

1)Lock顯示地獲取和釋放鎖:
	void lock(); // 獲取鎖。
	void unlock(); // 釋放鎖。
	
2)能夠非阻塞的獲取鎖:
	boolean tryLock(); // 調用該方法後馬上返回,若是能獲取到鎖則返回true,不然返回false;
	
3)能夠中斷地獲取鎖:
	void lockInterruptibly() throws InterruptedException; // 在鎖的獲取過程當中,若是獲取鎖的線程被中斷,則拋出中斷異常並返回。
	
4)能夠超時地獲取鎖:
	boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 在time時間內,獲取到鎖返回true,若是獲取鎖的線程被中斷則返回false;超出time時間,返回false。

5)Condition newCondition();

	Condition:java.util.concurrent.locks.Condition
	
		1)每一個Condition對象都包含着一個等待隊列,該隊列是Condition對象實現等待/通知功能的關鍵。
		2)等待隊列是一個FIFO的隊列,在隊列中的每一個節點都包含了一個線程引用,該線程就是在Condition對象上等待的線程。
		3)若是一個線程調用了Condition.await()方法,那麼該線程將會釋放鎖,而且構形成節點加入等待隊列並進入等待狀態。
		4)事實上,同步隊列(獲取Lock排隊)和等待隊列(獲取Condition排隊)中節點的類型都是同步器的靜態內部類AbstractQueuedSynchronizer.Node
		5)注意:在condition.await()方法調用以前,必須先用lock.lock()方法獲取到鎖。
		
6)多路通知功能:
	在一個Lock對象裏面建立多個Condition實例,線程對象能夠註冊在指定的Condition中,從而能夠有選擇地進行線程通知。能夠先對線程進行分組,而後再喚醒指定組中的線程。


實現:ReentrantLock、ReentrantReadWriteLock、CountDownLatch

2)ReentrantLock(重入鎖):併發

重入:
	1)重入是指任意線程在獲取到鎖以後可以再次獲取該鎖而不會被阻塞。
	2)在調用lock()方法時,已經獲取到鎖的線程,可以再次調用lock()方法獲取鎖而不被阻塞。


重入的過程:
	1)線程再次獲取鎖:鎖須要去識別獲取鎖的線程是否爲當前佔據鎖的線程,若是是,則再次成功獲取。
	2)鎖的最終釋放:
		1>獲取鎖時須要進行計數自增,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時表示鎖已經成功釋放。
		2>線程重複n次獲取了鎖,隨後在釋放n次該鎖後,其它線程可以獲取到該鎖。


支持獲取鎖時的公平性和非公平性選擇:
	1)公平鎖保證了鎖的獲取按照FIFO原則,其代價是須要進行大量的線程切換。
	2)非公平鎖雖然可能形成線程「飢餓」,可是極少的線程切換,保證了其更大的吞吐量。


ReentrantLock類中封裝的AQS的同步狀態表示鎖被一個線程重複獲取的次數。


舉例:
	------------------------------------------
	實現等待/通知的service類:
	import java.util.concurrent.locks.Condition;
	import java.util.concurrent.locks.Lock;
	import java.util.concurrent.locks.ReentrantLock;

	public class MessageService {
		
		private Lock lock = new ReentrantLock();
		public Condition conditionA = lock.newCondition();
		public Condition conditionB = lock.newCondition();
		
		public void awaitA() {
			try {
				lock.lock();
				System.out.println("start awaitA, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
				conditionA.await();
				System.out.println("end awaitA, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}
		
		public void awaitB() {
			try {
				lock.lock();
				System.out.println("start awaitB, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
				conditionB.await();
				System.out.println("end awaitB, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}
		
		public void signalAllforA() {
			try {
				lock.lock();
				System.out.println("signalAllforA, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
				conditionA.signalAll();
			} finally {
				lock.unlock();
			}
		}
		
		public void signalAllforB() {
			try {
				lock.lock();
				System.out.println("signalAllforB, time:" + System.currentTimeMillis() + " ThreadName:" + Thread.currentThread().getName());
				conditionB.signalAll();
			} finally {
				lock.unlock();
			}
		}
	}
	---------------------
	線程A
	public class ThreadA extends Thread{

		private MessageService messageService;
		
		public ThreadA(MessageService messageService) {
			this.messageService = messageService;
		}
		
		[@Override](https://my.oschina.net/u/1162528)
		public void run() {
			this.messageService.awaitA();
		}
	}
	---------------------
	線程B
	public class ThreadB extends Thread{

		private MessageService messageService;
		
		public ThreadB(MessageService messageService) {
			this.messageService = messageService;
		}
		
		[@Override](https://my.oschina.net/u/1162528)
		public void run() {
			this.messageService.awaitB();
		}
	}

	---------------------
	測試類:
		public class LockTest {

			public static void main(String[] args) {
				try {
					MessageService messageService = new MessageService();
					ThreadA threadA = new ThreadA(messageService);
					threadA.setName("Thread-A");
					threadA.start();
					ThreadB threadB = new ThreadB(messageService);
					threadB.setName("Thread-B");
					threadB.start();
					
					Thread.sleep(5000);
					messageService.signalAllforA(); // 運行後,只有ThreadA被喚醒了
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		結果:
			start awaitA, time:1497603307947 ThreadName:Thread-A
			start awaitB, time:1497603307947 ThreadName:Thread-B
			signalAllforA, time:1497603312947 ThreadName:main
			end awaitA, time:1497603312947 ThreadName:Thread-A

	------------------------------------------

3)ReadWriteLock(讀寫鎖):ide

概念:讀寫鎖維護了一對鎖,一個讀鎖和一個寫鎖,經過分離讀鎖和寫鎖,使得併發性相比通常的獨佔鎖有了很大提高。


源碼:

	package java.util.concurrent.locks;
		
	public interface ReadWriteLock {
	
		/** Returns the lock used for reading. */
		Lock readLock();

		/** Returns the lock used for writing. */
		Lock writeLock();
	}


說明:
	1)讀寫鎖在同一時刻能夠容許多個讀線程訪問,可是在寫線程訪問時,其它的讀線程和寫線程均被阻塞。
	2)若是當前線程獲取了寫鎖或者寫鎖未被獲取,則當前線程能夠獲取讀鎖。
	3)若是當前線程在獲取讀鎖時,寫鎖已被其餘線程獲取,則進入等待狀態。
	4)通常狀況下,讀寫鎖的性能都會比獨佔鎖好,由於大多數場景讀是多於寫的。
	5)在讀多於寫的狀況下,讀寫鎖可以提供比獨佔鎖更好的併發性和吞吐量。


實現:ReentrantReadWriteLock

	特色:
		1)支持公平性選擇
		2)支持讀鎖和寫鎖的重入
		3)支持鎖降級:
			1>鎖降級的概念:先獲取到寫鎖後,再獲取到讀鎖,隨後才釋放寫鎖,最後只持有讀鎖的過程。
			2>按照獲取寫鎖、獲取讀鎖、釋放寫鎖的順序進行操做,從而將寫鎖降級爲讀鎖。
			
	說明:
		1)若是存在讀鎖,則寫鎖不能被獲取,進入等待狀態。
		2)若是其它線程已經獲取了寫鎖,則當前線程不能獲取讀鎖,進入等待狀態。

		
讀寫狀態的設計:

	在一個整型變量上維護多種狀態,就須要「按位切割使用」這個變量,讀寫鎖將變量切分紅了兩個部分,高16位表示讀,低16位表示寫。
相關文章
相關標籤/搜索