1. 程式人生 > >同步鎖之lock

同步鎖之lock

ins gen 過程 blog nds 同步代碼塊 得到 代碼 override

一、 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