java併發系列之synchronized與lock

synchronized
package thread;

/**
 * synchronized修飾實例方法、靜態方法、代碼塊,修飾實例方法和代碼塊時候用this對象當鎖,修改靜態方法時用類的class對象當鎖	
 * synchronized是悲觀鎖,synchronized和ReentrantLock等獨佔鎖就是悲觀鎖思想的實現
 * synchronized是可重入鎖
 * synchronized是非公平鎖
 * 做爲鎖的條件:必須是對象且被多個線程共享才能做爲鎖
 *
 */
public class SynchronizedDemo {
	private int a;
	
	public int getA() {
		return a;
	}

	public void setA(int a) {
			
		this.a = a;
	}

	public void add(){
		synchronized(this){
			int x = getA();
			setA(x+1);
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		final SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
		
		for (int i = 0; i < 100000; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					
					synchronizedDemo.add();
				}
			}).start();
			
		}
		
		
		Thread.sleep(10000);
		System.err.println(synchronizedDemo.getA());
	}
}

 

lock
package thread;

import java.util.concurrent.locks.ReentrantLock;


/**
 * 一、鎖像synchronized同步塊同樣,是一種線程同步機制,但比Java中的synchronized同步塊更復雜,由於鎖(以及其它更高級的線程同步機制)
 * 是由synchronized同步塊的方式實現的
 * 
 * 二、樂觀鎖與悲觀鎖
 *    樂觀鎖適用於讀比較多的場景,悲觀鎖適用於寫比較多的場景,不加鎖會帶來大量的性能提高
 *    樂觀鎖常見的兩種實現方式:版本號機制或CAS算法實現
 * 	      版本號機制:通常是在數據表中加上一個數據版本號version字段,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,
 *    在提交更新時,若剛纔讀取到的version值爲當前數據庫中的version值相等時才更新,不然重試更新操做,直到更新成功	
 *
 * 三、CAS算法
 *    即 compare and swap(比較與交換),是一種有名的無鎖算法,即不使用鎖的狀況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的狀況下實現變量的同步,
 *    因此也叫非阻塞同步
 *  check and act模式,先檢查後操做模式	首先檢查一個變量的值,而後再基於這個值作一些操做,check then act操做必須是原子的。
 *  原子就是說」check「操做和」act「被當作一個原子代碼塊執行。不存在多個線程同時執行原子塊
 *  CAS有3個操做數,內存值V,預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作
 *  
 * 四、可重入鎖:假如一把鎖鎖了n個地方,那麼只要獲得這把鎖,那n個地方均可以訪問
 * 
 * 五、獨享鎖和共享鎖
 * 	獨享鎖:是指該鎖一次只能被一個線程所持有。實現:ReentrantLock
 *	共享鎖:是指該鎖可被多個線程所持有。實現:ReadWriteLock,讀鎖是共享鎖,寫鎖是獨享鎖。由於讀讀能夠同時進行,而讀寫、寫寫不能同時進行
 *
 * 六、公平鎖、非公平鎖:公平鎖是指多個線程按照申請鎖的順序來獲取鎖。
 * 
 * 七、分段鎖 
 * 分段鎖實際上是一種鎖的設計,並非具體的一種鎖,對於ConcurrentHashMap而言,其併發的實現就是經過分段鎖的形式來實現高效的併發操做
 * 當須要put元素的時候,並非對整個hashmap進行加鎖,而是先經過hashcode來知道他要放在哪個分段中,而後對這個分段進行加鎖,因此當多線程put的時候,
 * 只要不是放在一個分段中,就實現了真正的並行的插入
 * 
 * 八、偏向鎖/輕量級鎖/重量級鎖,這三種鎖是指鎖的狀態,而且是針對Synchronized
 * 
 * 九、AQS是抽象隊列同步器,維護了一個volatile int state(表明共享資源)和一個FIFO線程等待隊列(多線程爭用資源被阻塞時會進入此隊列)
 * 
 * 十、自旋鎖:在Java中,自旋鎖是指嘗試獲取鎖的線程不會當即阻塞,而是採用循環的方式去嘗試獲取鎖,這樣的好處是減小線程上下文切換的消耗,缺點是循環會消耗CPU
 * 獲取不到鎖的線程要麼阻塞,要麼自旋
 */
public class LockDemo {
	
	private int a;
	//經過構造函數指定是否爲公平鎖
	private ReentrantLock reentrantLock=new ReentrantLock(true);
	//private ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
	
	public int getA() {
		return a;
	}

	public void setA(int a) {
			
		this.a = a;
	}

	public void add(){
		
		reentrantLock.lock();
		//reentrantReadWriteLock.readLock().lock();
		
		try {
			int x = getA();
			setA(x+1);
		} finally {
			reentrantLock.unlock();
			//reentrantReadWriteLock.readLock().unlock();
		}
		
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		final LockDemo lockDemo = new LockDemo();
		
		
		for (int i = 0; i < 100000; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					
					lockDemo.add();
				}
			}).start();
			
		}
		
		
		Thread.sleep(10000);
		System.err.println(lockDemo.getA());
	}
}

 

二者區別
1.首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;

2.synchronized沒法判斷是否獲取到鎖,Lock能夠判斷是否獲取到鎖;

3.synchronized會自動釋放鎖(執行完代碼或發生異常會釋放鎖),Lock需在finally中手工釋放鎖,不然容易形成線程死鎖;

4.用synchronized關鍵字的兩個線程1和線程2,若是當前線程1得到鎖,線程2線程等待。若是線程1阻塞,線程2則會一直等待下去,而Lock鎖就不必定會等待下去,若是嘗試獲取不到鎖,線程能夠不用一直等待就結束了;

5.synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可中斷、能夠設置是否公平

6.Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少許的同步問題
相關文章
相關標籤/搜索