鎖、重入鎖與條件

Java中的鎖框架指的是java.util.concurrent.locks這個包裏的,不同於對象的內置加鎖同步以及java.lang.Object的等待/通知機制,包含鎖框架的併發工具通過輪詢鎖、限時等待及其他方式改善了這種機制。

這裏的鎖指的是接口Lock

方法

包含了如下幾種方法:

  • void lock():獲取鎖。當鎖不可用時,調用線程會被強制一直等待直到鎖可用。

  • void lockInterruptibly():除非調用線程被中斷,否則獲得鎖。當鎖不可用時,調用線程被強制一直等待,直到鎖可用或線程中斷。

  • Condition newCondition():返回一個新的綁定到當前鎖示例之上的Condition實例。當Lock的實現類不支持條件時,拋出異常。

  • boolean tryLock():在該方法被調用時,鎖可用則獲得鎖,當獲得鎖之後,該方法返回true;反之返回false。

  • boolean tryLock(long time, Time unit):當在制定等待時間內可用鎖可用並且調用線程沒被中斷則獲得鎖,等待時間以java.util.concurrent.TimeUnit計(秒、毫秒等等)。當鎖不可用時,調用線程會在指定時間內一直等待,直到鎖可用或線程中斷。中斷拋出異常。

  • void unlock():釋放鎖

使用方式

一般使用鎖我們需要用到下面這樣的形式:

Lock  lock = ...;// 實例化接口
        lock.lock();
        try {
            //使用鎖獲取到的資源
        } catch (Exception e) {
            //異常處理
        } finally {
            lock.unlock();
        }

重入鎖

類ReentranLock實現了接口Lock,這是一個可重入的互斥鎖,這個鎖是和一個持有量相關聯的。當一條線程持有這個鎖並且調用lock()、lockUnitinterruptibly()或者任意一個tryLock()方法重新獲取鎖,這個持有量就遞增1。當線程調用unlock()方法,持有量就遞減1。當持有量爲0時,鎖就會被釋放。

方法

ReentranLock中的方法其實就是實現了Lock接口中的方法,這裏需要說的是它的兩個構造方法:

  • ReentrantLock():創建一個ReentrantLock的實例,該構造函數等價於ReentrantLock(false)。

  • ReentrantLock(boolen fair):創建一個擁有指定公平策略的實例。當這個鎖需要使用公平的 排序策略 時,給fair複製爲true。 在爭用的環境下,這個鎖傾向於將訪問權限分配給 等待最久的線程

還有兩個ReentranLock自己的方法:

  • boolean isFair():返回是否有公平策略。

  • boolean isHeldByCurrentThread():返回鎖是否被當前線程持有。

注意,ReentranLock如果在沒有持有鎖的情況下調用unlock()會拋出IllegalMonitorStateException異常。

使用方法

示範如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class RLDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        final ReentrantLock lock = new ReentrantLock();
        class Worker implements Runnable {

            private final String name;

            Worker(String name) {
                this.name = name;
            }

            @Override
            public void run() {
                lock.lock();
                try {
                    if (lock.isHeldByCurrentThread()) {
                        System.out.printf("Thread %s entered critical section.%n", name);
                    }
                    System.out.printf("Thread %s performing work.%n", name);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                    System.out.printf("Thread %s finish work.%n", name);
                }finally {
                    lock.unlock();
                }
            }
        }

        executorService.execute(new Worker("ThreadA"));
        executorService.execute(new Worker("ThreadB"));
        try {
            executorService.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdownNow();
    }
}

結果爲:

.PNG

可以看到,兩條線程並不是交替執行的,當一個線程調用lock()方法而鎖又不可用時,這個線程就一直禁用,直到鎖可用(即鎖被持有鎖的進程釋放)。

條件

接口Condition把Object的wait和notification方法(wait(),notify()和notifyAll())分解到不同的條件對象中。通過把這些條件和任意Lock實現的使用組合起來,起到讓每個對象具有多重等待集合的作用。這裏Lock取代了同步方法、代碼塊,Condition取代了Object的wait、notification方法。

一個Condition的實例原本就會綁定到一個鎖上。可以使用Lock的newCondition()方法去獲取一個針對特定Lock實例的Condition實例。

方法

Condition聲明瞭下列方法:

  • void await():在接收到信號或者被中斷之前,強制調用線程一直等待。

  • boolean await(long time, TimeUnit unit):在接收到信號、被中斷,或者超過指定的等待時間之前,強制調用線程一直等待。

  • long awaitNanos(long nanosTimeout):同上。返回值爲等待結果時間的估算

  • void awaitUninterruptibly():在接收到信號之前,強制當前線程一直等待。

  • boolean awaitUntil(Date deadline):同上上上。

  • void signal():喚醒一個等待中的線程。

  • void signalAll():喚醒所有等待中的線程。

使用方法

  1. 共享變量
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Shared {
    private char c;
    private volatile boolean available;
    private final Lock lock;
    private final Condition condition;
    Shared() {
        available = false;
        lock = new ReentrantLock();
        condition = lock.newCondition();
    }

    Lock getLock() {
        return lock;
    }

    char getSharedChar() {
        lock.lock();
        try {
            while (!available) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            available = false;
            condition.signal();
        } finally {
            lock.unlock();
            return c;
        }
    }

    void setSharedChar(char c) {
        lock.lock();
        try {
            while (available) {
                try {
                    condition.await();
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
            this.c = c;
            available = true;
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
  1. 生產者
import java.util.concurrent.locks.Lock;

public class Producer extends Thread{
    private final Lock lock;
    private final Shared shard;
    Producer(Shared shard) {
        this.shard = shard;
        this.lock = shard.getLock();
    }

    @Override
    public void run() {
        for (char ch = 'A'; ch <= 'D'; ch++) {
            lock.lock();
            shard.setSharedChar(ch);
            System.out.println(ch + " produced by producer");
            lock.unlock();
        }
    }
}
  1. 消費者
import java.util.concurrent.locks.Lock;

public class Consumer extends Thread {
    private final Lock lock;
    private final Shared shard;

    Consumer(Shared shard) {
        this.shard = shard;
        lock = shard.getLock();
    }

    @Override
    public void run() {
        char c;
        do {
            lock.lock();
            c = shard.getSharedChar();
            System.out.println(c + " consumed by consumer.");
            lock.unlock();
        } while (c != 'D');
    }
}
  1. 調用
public class PC {
    public static void main(String[] args) {
        Shared shared = new Shared();
        new Producer(shared).start();
        new Consumer(shared).start();
    }
}

結果如下:

2.PNG

可以看到,生產者和消費者獲取到的鎖均爲共享變量當中的,而共享變量內部則使用條件變量來控制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章