ReentrantLock

ReentrantLock

可重入互斥锁具有与使用 synchronized 的隐式监视器锁具有相同的行为和语义,但具有更好扩展功能。ReentrantLock 由最后成功锁定的线程拥有,而且还未解锁。当锁未被其他线程占有时,线程调用 lock()将返回并且成功获取锁。如果当前线程已拥有锁,则该方法将立即返回。这可以使用方法 isHeldByCurrentThread()和 getHoldCount()来检查。

特性 ReentrantLock Synchronized
锁实现机制 依赖 AQS 监视器模式
灵活性 支持响应中断、超时、尝试获取锁 不灵活
释放形式 必须显示调用 unlock() 释放锁 自动释放监视器
锁类型 公平锁 & 非公平锁 非公平锁
条件队列 可关联多个条件队列 关联一个条件队列
可重入性 可重入 可重入

典型的 synchronized 与 ReentrantLock 对比如下:

// **************************Synchronized的使用方式**************************
// 1.用于代码块
synchronized (this) {}
// 2.用于对象
synchronized (object) {}
// 3.用于方法
public synchronized void test () {}
// 4.可重入
for (int i = 0; i < 100; i++) {
	synchronized (this) {}
}

// **************************ReentrantLock的使用方式**************************
public void test () throw Exception {
	// 1.初始化选择公平锁、非公平锁
	ReentrantLock lock = new ReentrantLock(true);
	// 2.可用于代码块
	lock.lock();
	try {
		try {
			// 3.支持多种加锁方式,比较灵活; 具有可重入特性
			if(lock.tryLock(100, TimeUnit.MILLISECONDS)){ }
		} finally {
			// 4.手动释放锁
			lock.unlock()
		}
	} finally {
		lock.unlock();
	}
}

使用 ReentrantLock

构造函数接受可选的 fairness 参数。当设置为 true 时,在竞争条件下,锁定有利于赋予等待时间最长线程的访问权限。否则,锁将不保证特定的访问顺序。在多线程访问的情况,使用公平锁比默认设置,有着更低的吞吐量,但是获得锁的时间比较小而且可以避免等待锁导致的饥饿。但是,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的许多线程中的一个可以连续多次获得它,而其他活动线程没有进展并且当前没有持有锁。不定时的 tryLock()方法不遵循公平性设置。即使其他线程正在等待,如果锁可用,它也会成功。

public class Test implements Runnable {

  public synchronized void get() {
    System.out.println(Thread.currentThread().getId());

    //在子方法里又进入了锁
    set();
  }

  public synchronized void set() {
    System.out.println(Thread.currentThread().getId());
  }

  @Override
  public void run() {
    get();
  }

  public static void main(String[] args) {
    Test ss = new Test();
    new Thread(ss).start();
    new Thread(ss).start();
    new Thread(ss).start();
  }
}

Links

上一页