并发编程中级二:Java 锁框架[ReentrantLock,Condition]

综述

Java中的锁,大致可以分为"同步锁"和"java.util.concurrent.locks中的锁"。

同步锁就是使用synchronized关键字来进行同步的锁机制,实现资源的互斥访问的锁。Java 1.0就有了.
java.util.concurrent.locks中的锁,都于java1.5进行的添加.相比同步锁,java.util.concurrent.locks中的锁的功能更强,它为锁和等待条件提供一个框架的接口和类.

java.util.concurrent中的锁,包括:
  • Lock接口,
  • ReentrantLock独占锁,
  • ReentrantReadWriteLock读写锁。
  • ReadWriteLock接口,
  • LockSupport阻塞原语,
  • Condition条件,
  • AbstractOwnableSynchronizer/AbstractQueuedSynchronizer/AbstractQueuedLongSynchronizer三个抽象类,
  • CountDownLatch,CyclicBarrier和Semaphore通过AQS来实现的.

Lock接口

Lock 实现提供了比使用 synchronized 方法和语句更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 
接口支持那些语义不同(重入、公平等)的锁规则。所谓语义不同,是指锁可是有"公平机制的锁"、"非公平机制的锁"、"可重入的锁"等等。"公平机制"是指"不同线程获取锁的机制是公平的",而"非公平机制"则是指"不同线程获取锁的机制是非公平的","可重入的锁"是指同一个锁能够被一个线程多次获取。
其子类有三个:ReentrantLock独占锁,ReentrantReadWriteLock读写锁。

互斥锁ReentrantLock

ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
// 创建一个 ReentrantLock ,默认是“非公平锁”。
ReentrantLock()
// 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。
ReentrantLock(boolean fair)


// 查询当前线程保持此锁的次数。
int getHoldCount()
// 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
protected Thread getOwner()
// 返回一个 collection,它包含可能正等待获取此锁的线程。
protected Collection<Thread> getQueuedThreads()
// 返回正等待获取此锁的线程估计数。
int getQueueLength()
// 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 返回等待与此锁相关的给定条件的线程估计数。
int getWaitQueueLength(Condition condition)
// 查询给定线程是否正在等待获取此锁。
boolean hasQueuedThread(Thread thread)
// 查询是否有些线程正在等待获取此锁。
boolean hasQueuedThreads()
// 查询是否有些线程正在等待与此锁有关的给定条件。
boolean hasWaiters(Condition condition)
// 如果是“公平锁”返回true,否则返回false。
boolean isFair()
// 查询当前线程是否保持此锁。
boolean isHeldByCurrentThread()
// 查询此锁是否由任意线程保持。
boolean isLocked()
// 获取锁。
void lock()
// 如果当前线程未被中断,则获取锁。
void lockInterruptibly()
// 返回用来与此 Lock 实例一起使用的 Condition 实例。
Condition newCondition()
// 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
boolean tryLock()
// 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。
boolean tryLock(long timeout, TimeUnit unit)
// 试图释放此锁。
void unlock()

Condition-线程通信更高效的方式

Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
换言之:Condition能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,
假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";
当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;
当缓冲区为空时,"读线程"需要等待。         
如果采用Object类中的wait(), notify(),notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。  
但是,通过Condition,就能明确的指定唤醒读线程。
// 造成当前线程在接到信号或被中断之前一直处于等待状态。
void await()
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit)
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout)
// 造成当前线程在接到信号之前一直处于等待状态。
void awaitUninterruptibly()
// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
boolean awaitUntil(Date deadline)
// 唤醒一个等待线程。
void signal()
// 唤醒所有等待线程。
void signalAll()

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。 

两者结合的实例:

package org.credo.jdk.thread.jdklock;

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

/**
 * 卡额度最高100.初始卡内钱为0. 每次消费不能超过卡内余额.存钱不能超过最高额度.
 */
public class TestReentrantLock
{
	private int money = 0;// 当前卡内金额
	private int maxMoney = 100;// 当前卡最高额度
	private Lock lock = new ReentrantLock();
	private Condition conditionProducer = lock.newCondition();
	private Condition conditionCustomer = lock.newCondition();

	public static void main(String[] args)
	{
		TestReentrantLock trl = new TestReentrantLock();
		ProducerA p = new ProducerA(trl);
		CustomerA c = new CustomerA(trl);

		p.produce(60);
		p.produce(120);
		c.consume(90);
		c.consume(150);
		p.produce(110);
	}

	// 生产者金额不能超过100
	public void produce(int val)
	{
		lock.lock();
		try
		{
			int currentAmount = val;
			while (currentAmount > 0)
			{
				while (money >= maxMoney)
				{
					conditionProducer.await();
				}
				// 实际存款,如果存款数超过上限,就取能存入的最大额
				int actualDeposit = money + currentAmount > maxMoney ? maxMoney - money : currentAmount;
				money = actualDeposit + money;
				currentAmount = currentAmount - actualDeposit;
				System.out.println(Thread.currentThread().getName() + " 存款 " + val + ",现存金额:" + money);
				conditionCustomer.signal();
			}
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		} finally
		{
			lock.unlock();
		}
	}

	// 消费者金额不能超过总额
	public void consume(int val)
	{
		lock.lock();
		try
		{
			int currentAmount = val;
			while (currentAmount > 0)
			{
				while (this.money <= 0)
				{
					conditionCustomer.await();
				}
				// 实际消费,如果消费额大于现有金额,就实际消费为现有金额.
				int actualConsumption = money < currentAmount ? money : currentAmount;
				this.money = money - actualConsumption;// 减去实际消费额
				currentAmount = currentAmount - actualConsumption;
				System.out.println(Thread.currentThread().getName() + "刷卡消费" + val + ",剩余金额:" + money);
				conditionProducer.signal();
			}
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		} finally
		{
			lock.unlock();
		}
	}
}

class ProducerA
{
	private TestReentrantLock test;

	public ProducerA(TestReentrantLock test)
	{
		this.test = test;
	}

	public void produce(final int number)
	{
		new Thread()
		{
			public void run()
			{
				test.produce(number);
			}
		}.start();
	}
}

class CustomerA
{
	private TestReentrantLock test;

	public CustomerA(TestReentrantLock test)
	{
		this.test = test;
	}

	public void consume(final int number)
	{
		new Thread()
		{
			public void run()
			{
				test.consume(number);
			}
		}.start();
	}
}
学习参考自:http://www.cnblogs.com/skywang12345/p/3496101.html



你可能感兴趣的:(并发编程中级二:Java 锁框架[ReentrantLock,Condition])