Google guava 中的Monitor

synchronized

自从Java提供了多线程编程,咱们常常须要处理这样的状况:在特定的时间,咱们须要限制访问,确保只有一个线程访问咱们的代码。Java提供了同步关键字synchronized来实现这种访问控制,可是使用synchronized会存在一些问题。第一个问题是,当咱们须要调用线程的wait()方法时,咱们必须记得去使用while循环。看下面例子,来自guava monitor api上的说明:java

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,咱们使用它来实现上面的场景看看api

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中释放锁google

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。
相关文章
相关标签/搜索