歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Google Guava 中的Monitor

Google Guava 中的Monitor

日期:2017/3/1 9:30:30   编辑:Linux編程

synchronized

自從Java提供了多線程編程,我們經常需要處理這樣的情況:在特定的時間,我們需要限制訪問,確保只有一個線程訪問我們的代碼。Java提供了同步關鍵字synchronized來實現這種訪問控制,但是使用synchronized會存在一些問題。第一個問題是,當我們需要調用線程的wait()方法時,我們必須記得去使用while循環。

看下面例子,來自guava monitor api上的說明:

 public class SafeBox<V> {
    private V value;

    public synchronized V get() throws InterruptedException {
        while (value == null) {
            wait();
        }
        V result = value;
        value = null;
        notifyAll();
        return result;
    }

    public synchronized void set(V newValue) throws InterruptedException {
        while (value != null) {
            wait();
        }
        value = newValue;
        notifyAll();
    }
}

在這個例子中獲取一個值,當值不存在的時候,我們等待。。。有值的時候需要notifyAll()。這裡需要注意的是,我們要在while循環中使用wait方法,而不是if。另外用notifyAll而不是notify。

ReentrantLock

在java.util.concurrent包中提供了ReentrantLock,我們使用它來實現上面的場景看看

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

public class SafeBox<V> {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition valuePresent = lock.newCondition();
    private final Condition valueAbsent = lock.newCondition();
    private V value;

    public V get() throws InterruptedException {
        lock.lock();
        try {
            while (value == null) {
                valuePresent.await();
            }
            V result = value;
            value = null;
            valueAbsent.signal();
            return result;
        } finally {
            lock.unlock();
        }
    }

    public void set(V newValue) throws InterruptedException {
        lock.lock();
        try {
            while (value != null) {
                valueAbsent.await();
            }
            value = newValue;
            valuePresent.signal();
        } finally {
            lock.unlock();
        }
    }
} 

我們依然需要使用while循環,但是有一個好處,我們可以定義兩個Condition,這樣我們就可以用signal來替代signalAll,這樣可能會帶來一點性能上的提升。

Monitor

Monitor是一個支持任意布爾條件的同步的抽象,Monitor類是作為ReentrantLock的一個替代,代碼中使用Monitor比使用ReentrantLock更不易出錯,可讀性也更強,並且也沒有顯著的性能損失,使用Monitor甚至有潛在的性能得到優化,下面我們看看用guava中的Monitor怎麼重寫上面的代碼

package com.hupengcool.guava.concurrency;

import com.google.common.util.concurrent.Monitor;

public class SafeBox<V> {
    private final Monitor monitor = new Monitor();
    private final Monitor.Guard valuePresent = new Monitor.Guard(monitor) {
        public boolean isSatisfied() {
            return value != null;
        }
    };
    private final Monitor.Guard valueAbsent = new Monitor.Guard(monitor) {
        public boolean isSatisfied() {
            return value == null;
        }
    };
    private V value;

    public V get() throws InterruptedException {
        monitor.enterWhen(valuePresent);
        try {
            V result = value;
            value = null;
            return result;
        } finally {
            monitor.leave();
        }
    }

    public void set(V newValue) throws InterruptedException {
        monitor.enterWhen(valueAbsent);
        try {
            value = newValue;
        } finally {
            monitor.leave();
        }
    }
}

可以發現使用guava之後,我們不需要使用while,使用Monitor.Guard定義進入代碼快的條件即可,代碼變得更加容易閱讀,寫起來也更加方便。
當我們Monitor的方法返回boolean值的時候,我們在if塊中包含try-finally塊,確保鎖能夠釋放。

if(monitor.enterIf(guard)){
    try{
       ...work..
    }finally{
        monitor.leave();
    }
}else{
   .. monitor not available..
}

當monitor的方法不返回任何值的時候,我們的代碼也需要在finally中釋放鎖

monitor.enter()
try{
    ...work..
}finally{
    monitor.leave();
}

Monitor有幾個常用的方法

  • enter():進入到當前Monitor,無限期阻塞,等待鎖。
  • enter(long time, TimeUnit unit):進入到當前Monitor,最多阻塞給定的時間,返回是否進入Monitor。
  • tryEnter():如果可以的話立即進入Monitor,不阻塞,返回是否進入Monitor。
  • enterWhen(Guard guard):進入當前Monitor,等待Guard的isSatisfied()為true後,繼續往下執行 ,但可能會被打斷。
  • enterIf(Guard guard):如果Guard的isSatisfied()為true,進入當前Monitor。等待獲得鎖,不需要等待Guard satisfied。
  • tryEnterIf(Guard guard):如果Guard的isSatisfied()為true並且可以的話立即進入Monitor,不等待獲取鎖,也不等待Guard satisfied。

Copyright © Linux教程網 All Rights Reserved