同步鎖之lock
一、 synchronized的缺陷
當一個代碼塊被synchronized修飾時,同時該代碼塊被一個線程執行,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這裏獲取鎖的線程釋放鎖只會有兩種情況:
1)獲取鎖的線程執行完成代碼塊,自動釋放鎖資源
2)代碼塊中執行異常,由jvm釋放鎖資源
如果在執行代碼塊時候,一個線程在代碼塊執行過程中Thread.sleep() 睡眠 線程被阻塞了,其他線程只能等待當前線程執行完成後才能執行。及其浪費效率。
另外當多個線程在執行寫與寫操作時,會發生線程沖突,但當多個線程用於讀操作,其中一個線程讀取占用的鎖資源 而其他線程只能等待造成浪費資源。
通過多個線程讀取操作線程不會沖突,通過lock可以實現。
不同點:
synchronized是java內置的,是java關鍵字,lock 是一個接口 不是內置的通過這個接口可以實現同步訪問
synchronized不需要用戶手動釋放鎖資源,當同步方法或者同步代碼塊執行完成後,系統會自動讓線程釋放鎖資源。而Lock必須要手動釋放鎖資源,如果不釋放會出現死鎖
二、 java.util.concurrent.locks包下常用的類
1) Lock 是一個接口
提供了六個方法
void lock(); boolean tryLock(); boolean tryLock(long time,timeUnit unit); void lockInteruptibly(); contition newContition() void unlock();
其中lock() tryLock() tryLock(long time,timeUnit unit) lockInteruptibly() 用於獲取鎖 unlock() 用於釋放鎖
由於采用lock 不會自動釋放鎖,需要手動釋放鎖 所以采用lock 必須在try{}catch(){}finally{}異常捕獲中進行,在finally中釋放鎖
lock()是最常用的獲取鎖的方式
例子:
Lock lock = ..
lock.lock();
try{
}catch(Exception e){
}finally{
lock.unlock()
}
tryLock 嘗試獲取鎖,如果獲取到返回true 如果獲取不到返回false 不會拿不到鎖一致在等待
tryLock(long time,TimeUnit unit) 與tryLock()一樣都是嘗試獲取鎖,如果獲取到返回true 否則返回false 區別在於tryLock(long time ,TimeUnit unit)會等待一定時間,如果直接獲取到鎖返回true 或者在設置的等待時間內獲取到鎖返回true 否則返回flase
Lock lock = ..
if(lock.tryLock()){
try{
}catch(){
}finally{
lock.unlock();
}
}
lockInterruptibly() 可以用來獲取鎖,但是如果在獲取的鎖已經被其他線程鎖占用 在等待過程中可以中斷當前線程的等待狀態。通俗的將 如果 a b兩個線程通過Lock.lockInterruptibly()獲取某一個鎖時,如果a先獲取到了鎖 b處於等待狀態 那麽b可以調用b.interrup()方法中斷b的等待狀態
由於lockInterruptibly()的聲明中拋出了異常,所以lock.lockInterruptibly()必須放在try塊中或者在調用lockInterruptibly()的方法外聲明拋出InterruptedException。
public void method () throws InterruptedException{
lock.Interruptibly();
try{
}catch(){
}finally{
lock.unlock();
}
}
當一個線程獲取到了鎖 是不會被interrup()方法中斷 只有處於等待狀態的線程在才能被中斷。
2)ReadWriteLock 可重入鎖
ReadWriteLock是Lock接口的唯一實現類
Lock 接口的實現方式:
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); public static void main(String[] args) { //實例化當前類對象 final LockDemo ld = new LockDemo(); //創建兩個線程對象 調用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ //創建局部lock鎖對象 Lock lock = new ReentrantLock(); System.out.println(lock); //獲取鎖 lock.lock(); try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"獲取了鎖"); } catch (Exception e) { // TODO: handle exception }finally{ //釋放鎖 lock.unlock(); System.out.println(Thread.currentThread().getName()+"釋放了鎖"); } } }View Code
運行結果:
java.util.concurrent.locks.ReentrantLock@100363[Unlocked]
java.util.concurrent.locks.ReentrantLock@e0a386[Unlocked]
Thread-0獲取了鎖
Thread-1獲取了鎖
Thread-0釋放了鎖
Thread-1釋放了鎖
產生這種結果的原因: 當多個線程獲取同一個對象的方法中的局部變量,每一個線程都會獲取一個局部變量lock的副本 通過打印鎖對象地址可以發現當前鎖對象是不同的鎖 所以他們可以獲取不同的鎖
解決這種方案:
將獲取鎖對象成員變量 屬於當前對象
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); //創建局部lock鎖對象 private Lock lock = new ReentrantLock(); public static void main(String[] args) { //實例化當前類對象 final LockDemo ld = new LockDemo(); //創建兩個線程對象 調用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ System.out.println(lock); //獲取鎖 lock.lock(); try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"獲取了鎖"); } catch (Exception e) { // TODO: handle exception }finally{ //釋放鎖 lock.unlock(); System.out.println(Thread.currentThread().getName()+"釋放了鎖"); } } }View Code
運行結果:
java.util.concurrent.locks.ReentrantLock@bcda2d[Unlocked]
java.util.concurrent.locks.ReentrantLock@bcda2d[Locked by thread Thread-0]
Thread-0獲取了鎖
Thread-0釋放了鎖
Thread-1獲取了鎖
Thread-1釋放了鎖
tryLock()
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); //創建局部lock鎖對象 private Lock lock = new ReentrantLock(); public static void main(String[] args) { //實例化當前類對象 final LockDemo ld = new LockDemo(); //創建兩個線程對象 調用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ System.out.println(lock); //獲取鎖 if(lock.tryLock()){ try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"獲取了鎖"); } catch (Exception e) { // TODO: handle exception }finally{ //釋放鎖 lock.unlock(); System.out.println(Thread.currentThread().getName()+"釋放了鎖"); } }else{ System.out.println(Thread.currentThread().getName()+"沒有獲取到鎖"); } } }View Code
運行結果:
java.util.concurrent.locks.ReentrantLock@14e8cee[Unlocked]
java.util.concurrent.locks.ReentrantLock@14e8cee[Locked by thread Thread-0]
Thread-1沒有獲取到鎖
Thread-0獲取了鎖
Thread-0釋放了鎖
當多個線程獲取同一個鎖對象時,當線程a 獲取鎖 b嘗試獲取鎖失敗返回false
lockInterruptibly()
package cn.ac.bcc.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class InterruptiblyDemo { Lock lock = new ReentrantLock(); public static void main(String[] args) { InterruptiblyDemo interruptiblyDemo = new InterruptiblyDemo(); MyThread myThread = new MyThread(interruptiblyDemo); MyThread myThread2 = new MyThread(interruptiblyDemo); myThread.start(); myThread2.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //中斷線程2 myThread2.interrupt(); } public void insert() throws InterruptedException{ lock.lockInterruptibly(); try{ System.out.println(Thread.currentThread().getName()+"得到了鎖"); long startTime = System.currentTimeMillis(); for(;;){ if(System.currentTimeMillis()-startTime>=Integer.MAX_VALUE){ break; } } }catch(Exception e){ e.printStackTrace(); }finally{ System.out.println(Thread.currentThread().getName()+"釋放了鎖"); lock.unlock(); } } } class MyThread extends Thread{ InterruptiblyDemo interruptiblyDemo; public MyThread(InterruptiblyDemo interruptiblyDemo){ this.interruptiblyDemo=interruptiblyDemo; } @Override public void run(){ try { interruptiblyDemo.insert(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(Thread.currentThread().getName()+"鎖被中斷"); } } }View Code
運行結果:
Thread-0得到了鎖
java.lang.InterruptedException
Thread-1鎖被中斷
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at cn.ac.bcc.lock.InterruptiblyDemo.insert(InterruptiblyDemo.java:30)
at cn.ac.bcc.lock.MyThread.run(InterruptiblyDemo.java:60)
https://www.cnblogs.com/baizhanshi/p/6419268.html
同步鎖之lock