1. 程式人生 > >執行緒併發五:執行緒安全之重入鎖

執行緒併發五:執行緒安全之重入鎖

  1. 重入鎖簡單介紹

    之前介紹的synchronized關鍵字是一種最簡單的控制方法。下面說一說執行緒安全的另一種實現方式——–重入鎖
    重入鎖使用java.util.concurrent.loks.ReentrantLock 類來實現
    a. lock() //獲得鎖,如果鎖已被佔用,則等待
    b. lockInterruptibly() //獲得鎖,但優先響應中斷
    c. tryLock() //嘗試獲得鎖,如果成功返回true.失敗返回false.
    d. tryLock(long time, TimeUnit nuit) //在指定時間內嘗試獲取鎖
    e. unlock() //釋放鎖
    f. isHeldByCurrentThread() //檢測當前執行緒是否持有鎖。

import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

public class ReenterLockSimpleTest implements Runnable {

    public static ReentrantLock lock = new ReentrantLock();

    public void sayHello(){
        lock.lock();
        lock.lock();
        try {
            System.out.println("["
+ new Date().getTime() + "]" + Thread.currentThread().getName() + ": say hello!!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } finally
{ lock.unlock(); lock.unlock(); } } public void run(){ while(true){ sayHello(); } } public static void main(String[] args){ Thread t1 = new Thread(new ReenterLockSimpleTest(),"test1"); Thread t2 = new Thread(new ReenterLockSimpleTest(),"test2"); t1.start(); t2.start(); } }

從列印可以看出,1秒只打印一條,兩個執行緒相互交替。重入鎖有著明顯的操作痕跡,何時加鎖,何時釋放鎖。也正是這樣,重入鎖對邏輯控制的靈活性要遠遠高於synchronized,重入鎖之所以叫重入鎖,那是因為這種鎖可以反覆進入的,當然這裡的反覆進入僅僅侷限於同一個執行緒。並且,釋放所得次數要是獲取鎖的次數一樣。

2.中斷響應
對於synchronized來說,如果一個執行緒在等待鎖,只有兩種情況,要麼得到鎖,要麼繼續等待,而重入鎖提供另一種可能,那就是執行緒中斷。

import java.util.concurrent.locks.ReentrantLock;

public class InterruptedReenterLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            if(!Thread.currentThread().isInterrupted()){
                if("test1".equals(Thread.currentThread().getName())){
                    lock1.lockInterruptibly();

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO: handle exception
                    }

                    lock2.lockInterruptibly();
                }else {
                    lock2.lockInterruptibly();

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO: handle exception
                    }

                    lock1.lockInterruptibly();
                }
            }

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(lock1.isHeldByCurrentThread()){
                lock1.unlock();
            }

            if(lock2.isHeldByCurrentThread()){
                lock2.unlock();
            }

            System.out.println(Thread.currentThread().getName() + " 執行緒退出!");
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Thread(new InterruptedReenterLock(),"test1");
        Thread t2 = new Thread(new InterruptedReenterLock(),"test2");

        t1.start();
        t2.start();

        Thread.sleep(2000);
        t2.interrupt();
    }
}

如上程式碼會出現兩個執行緒相互等待的情況,當執行緒2發生中斷後,釋放所有的鎖,執行緒1線上程2釋放鎖之後獲得鎖,兩個執行緒先後結束。

3.限時申請鎖
tryLock() 方法限時等待,有兩種方式, 一種為限時申請鎖,需要設定時間, 一種為嘗試獲取鎖,無論成功還是失敗直接返回,不等待。

import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockTiem implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public void sayHello(){
        try {
            if(lock.tryLock(4, TimeUnit.SECONDS)){
                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");
                Thread.sleep(6000);
            }else{
                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    public void run(){
        while(true){
            sayHello();
        }
    }

    public static void main(String[] args){
        Thread t1 = new Thread(new TryLockTiem(),"test1");
        Thread t2 = new Thread(new TryLockTiem(),"test2");
        t1.start();
        t2.start();
    }
}
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockTiem implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public void sayHello(){
        try {
            if(lock.tryLock()){
                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");
                Thread.sleep(2000);
            }else{
                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(lock.isHeldByCurrentThread()){
                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": release lock!!");
                lock.unlock();
            }
        }
    }

    public void run(){
        while(true){
            sayHello();
        }
    }

    public static void main(String[] args){
        Thread t1 = new Thread(new TryLockTiem(),"test1");
        Thread t2 = new Thread(new TryLockTiem(),"test2");
        t1.start();
        t2.start();
    }
}