深入理解ReentrantLock和Condition

全文概要

本篇将着手线程并发库,即java.util.concurrent包中的几个重要类。线程并发库是jdk1.5引入的,并发库的引入使得多线程开发更加的灵活多变,除此之外,因为java是面向对象的语言,线程并发库的引入让java多线程编程更加正统。本文主要内容如下:

  1. 介绍LockCondition接口;
  2. 通过使用Lock和Condition来模拟生产者/消费者模型,可以和传统线程实现生产者/消费者模型进行比较阅读。

Lock接口

Lock接口是线程并发库中锁对象的父接口,本文将通过他的实现类ReentrantLock来说明,ReentrantLock是一个可重入的互斥锁,也被称之为“独占锁”,顾名思义,ReentrantLock在同一时刻只能被一个线程拥有,reentrant英文释义就是可重入的,意义就是它可以被单个线程多次获取,相当于synchronized关键字的作用,只不过ReentrantLock是基于对象的。

Condition接口

Condition的作用是对锁进行更加精确的控制,Condition中的await()/signal()/signalAll和Object中wait()/notify()/notifyAll()方法有异曲同工之妙,不同的是Object中的三个方法是基于synchronized关键字,而Condition中的三个方法则需要和Lock联合使用。针对一个Lock对象可以有多个Condition对象,这就是Condition对象更加灵活的原因。

生产者/消费者模型案例

  • 代码案例

仓库模型代码:

package com.tml.javaCore.concurrent.lock;

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

/**
 * 

* 多线程 生产者-消费者仓库模型 * * @author Administrator * */ public class Repository { /* * 仓库的容量 */ private int capacity; /* * 仓库的实际容量 */ private int size; /* * 独占锁对象,相当于synchronized */ private Lock lock; /* * 仓库满时的条件 */ private Condition fullCondition; /* * 仓库空时的条件 */ private Condition emptyCondition; public Repository(int capacity) { this.capacity = capacity; this.size = 0; lock = new ReentrantLock(); fullCondition = lock.newCondition(); emptyCondition = lock.newCondition(); } /** *

* 生产者生产资源 * * @param produceAmount * 生产者预生产量 */ public void produce(int produceAmount) { //上锁 lock.lock(); try { while (produceAmount > 0) { while (size >= capacity) { System.out.println("has fulled!"); //仓库已经满了,相当于wait()操作,只有当fullCondition.signalAll()才能继续执行 fullCondition.await(); } /* * 获取实际的生产量 */ int actAmount = (size + produceAmount) > capacity ? capacity - size : produceAmount; size += actAmount; System.out.println( Thread.currentThread().getName() + ":has + :" + actAmount + ",the actual size is:" + size); produceAmount -= actAmount; // 通知消费者来消费,相当于notifyAll()操作 emptyCondition.signalAll(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { //释放锁一般放在finally中 lock.unlock(); } } /** *

* 消费者消费资源 * * @param consumeAmount * 消费者预消费量 */ public void consume(int consumeAmount) { lock.lock(); try { while (consumeAmount > 0) { while (size <= 0) { System.out.println("empty!"); emptyCondition.await(); } /* * 获取实际的消费量 */ int actAmount = (size < consumeAmount) ? size : consumeAmount; size -= actAmount; System.out.println( Thread.currentThread().getName() + ":has - :" + actAmount + ",the actual size is:" + size); consumeAmount -= actAmount; // 通知生产者来生产 fullCondition.signalAll(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } @Override public String toString() { return "Repository [capacity=" + capacity + ", size=" + size + "]"; } }

测试类代码:

package com.tml.javaCore.concurrent.lock;
/**
 * 

生产者-消费者 * @author Administrator * */ public class TestDemo { public static void main(String[] args) { //新建一个容量为100的仓库 Repository repository =new Repository(100); //模拟生产者,每隔1000毫秒生产23个产品 new Thread(new Runnable() { public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } repository.produce(23); } } },"producer1").start(); //模拟消费者1,每隔2500毫秒消费产品25 new Thread(new Runnable() { public void run() { while(true){ try { Thread.sleep(2500); } catch (InterruptedException e) { e.printStackTrace(); } repository.consume(25); } } },"consumer1").start(); //模拟消费者2,每隔1500毫秒消费产品10 new Thread(new Runnable() { public void run() { while(true){ try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } repository.consume(10); } } },"consumer2").start(); } }

  • 结果输出

下面是某一次的部分结果输出:

    producer1:has + :23,the actual size is:23
    consumer2:has - :10,the actual size is:13
    producer1:has + :23,the actual size is:36
    consumer1:has - :25,the actual size is:11
    producer1:has + :23,the actual size is:34
    consumer2:has - :10,the actual size is:24
    producer1:has + :23,the actual size is:47
    consumer2:has - :10,the actual size is:37
    consumer1:has - :25,the actual size is:12
    producer1:has + :23,the actual size is:35
    producer1:has + :23,the actual size is:58
    consumer2:has - :10,the actual size is:48
    producer1:has + :23,the actual size is:71
    consumer1:has - :25,the actual size is:46
    consumer2:has - :10,the actual size is:36
    producer1:has + :23,the actual size is:59
    producer1:has + :23,the actual size is:82
    consumer2:has - :10,the actual size is:72
    consumer1:has - :25,the actual size is:47
    producer1:has + :23,the actual size is:70
    consumer2:has - :10,the actual size is:60
    producer1:has + :23,the actual size is:83
    producer1:has + :17,the actual size is:100
    has fulled!
    consumer2:has - :10,the actual size is:90
    producer1:has + :6,the actual size is:96
    consumer1:has - :25,the actual size is:71
    producer1:has + :23,the actual size is:94
    consumer2:has - :10,the actual size is:84
    producer1:has + :16,the actual size is:100
    has fulled!

    consumer1:has - :25,the actual size is:75

  • 结果分析
  1. 仓库类中定义了一个锁对象,相当于synchronized的作用,由一个锁对象创建出两个条件对象fullCondition(仓库满)和emptyCondition(仓库空);
  2. 当生产者生产数据的时候,会调用lock.lock()上锁,当生产数量要超过仓库的最大容量的时候,会执行fullCondition.await(),相当于wait()的作用,只有执行fullCondition.signalAll()才能让当前线程继续执行。当生产数量没有超过仓库的最大容量的时候,会调用emptyCondition.signalAll()通知消费者来消费数据;
  3. 当消费者消费数据的时候,会调用lock.lock()上锁,当消费数据量大于仓库的实际容量的时候,会执行emptyCondition.await(),让消费者线程阻塞,只有线程调用emptyCondition.signalAll()才能让当前线程继续执行。当消费数据量小于仓库的实际容量的时候,会调用fullCondition.signalAll()通知生产者产生数据;
  4. 最后,当线程的任务执行完毕后,需要调用lock.unlock()释放对象锁。一般释放对象锁都写在finally中,主逻辑写在try中。这就是使用Lock和Condition来实现生产者/消费者模型的案例,后续还会使用阻塞队列的方式实现。

你可能感兴趣的:(多线程)