package com.test;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; // LockTest1.java
// 仓库
class Depot {
private int size; // 仓库的实际数量
private Lock lock; // 独占锁 public Depot() {
this.size = 0;
this.lock = new ReentrantLock();
} public void produce(int val) {
lock.lock();
try {
size += val;
System.out.printf("%s produce(%d) --> size=%d\n",
Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
} public void consume(int val) {
lock.lock();
try {
size -= val;
System.out.printf("%s consume(%d) <-- size=%d\n",
Thread.currentThread().getName(), val, size);
} finally {
lock.unlock();
}
}
}; // 生产者
class Producer {
private Depot depot; public Producer(Depot depot) {
this.depot = depot;
} // 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
} // 消费者
class Customer {
private Depot depot; public Customer(Depot depot) {
this.depot = depot;
} // 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
} public class LockTest1 {
public static void main(String[] args) {
Depot mDepot = new Depot();
Producer mPro = new Producer(mDepot);
Customer mCus = new Customer(mDepot);
//4个线程,2个线程操作produce方法,2个线程操作consume方法。4个线程使用同一个锁,每次只能由一个线程执行。
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
}
}
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。

顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。“锁”是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。
Condition函数列表

复制代码
// 造成当前线程在接到信号或被中断之前一直处于等待状态。
void await()
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit)
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout)
// 造成当前线程在接到信号之前一直处于等待状态。
void awaitUninterruptibly()
// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
boolean awaitUntil(Date deadline)
// 唤醒一个等待线程。
void signal()
// 唤醒所有等待线程。
void signalAll()
Condition示例

示例1是通过Object的wait(), notify()来演示线程的休眠/唤醒功能。
示例2是通过Condition的await(), signal()来演示线程的休眠/唤醒功能。
示例3是通过Condition的高级功能。 示例1 复制代码
public class WaitTest1 { public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
try {
System.out.println(Thread.currentThread().getName()+" start ta");
ta.start(); System.out.println(Thread.currentThread().getName()+" block");
ta.wait(); // 等待 System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} static class ThreadA extends Thread{ public ThreadA(String name) {
super(name);
} public void run() {
synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
System.out.println(Thread.currentThread().getName()+" wakup others");
notify(); // 唤醒“当前对象上的等待线程”
}
}
}
}
复制代码 示例2 复制代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; public class ConditionTest1 { private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition(); public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName()+" start ta");
ta.start(); System.out.println(Thread.currentThread().getName()+" block");
condition.await(); // 等待,会释放锁, System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} static class ThreadA extends Thread{ public ThreadA(String name) {
super(name);
} public void run() {
lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName()+" wakup others");
condition.signal(); // 唤醒“condition所在锁上的其它线程”
} finally {
lock.unlock(); // 释放锁
}
}
}
}
复制代码
运行结果: main start ta
main block
ta wakup others
main continue
通过“示例1”和“示例2”,我们知道Condition和Object的方法有一下对应关系: Object Condition
休眠 wait await
唤醒个线程 notify signal
唤醒所有线程 notifyAll signalAll
Condition除了支持上面的功能之外,它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。 如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。
看看下面的示例3,可能对这个概念有更深刻的理解。

满的时候只能减,空的时候只能加。加和减是2个线程组。

package com.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition; // LockTest3.java
// 仓库
class Depot1 {
private int capacity; // 仓库的容量
private int size; // 仓库的实际数量
private Lock lock; // 独占锁
private Condition fullCondtion; // 生产条件
private Condition emptyCondtion; // 消费条件 public Depot1(int capacity) {
this.capacity = capacity;
this.size = 0;
this.lock = new ReentrantLock();
this.fullCondtion = lock.newCondition();
this.emptyCondtion = lock.newCondition();
} public void produce(int val) {
lock.lock();
try {
// left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
while (val > 0) {
// 库存已满时,等待“消费者”消费产品。
while (size >= capacity)
fullCondtion.await(); //等待,释放锁
// 获取“实际生产的数量”(即库存中新增的数量)
// 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
// 否则“实际增量”=“想要生产的数量”
size += val;
System.out.printf("%s produce --> val=%3d, size=%3d\n",
Thread.currentThread().getName(), val, size);
// 通知“消费者”可以消费了。
emptyCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
} public void consume(int val) {
lock.lock();
try {
// left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
while (val > 0) {
// 库存为0时,等待“生产者”生产产品。
while (size <= 0)
emptyCondtion.await();
// 获取“实际消费的数量”(即库存中实际减少的数量)
// 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
// 否则,“实际消费量”=“客户要消费的数量”。
size -= val;
System.out.printf("%s consume<-- val=%3d, size=%3d\n",
Thread.currentThread().getName(), val, size);
fullCondtion.signal();
}
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
} public String toString() {
return "capacity:"+capacity+", actual size:"+size;
}
}; // 生产者
class Producer1 {
private Depot1 depot; public Producer1(Depot1 depot) {
this.depot = depot;
} // 消费产品:新建一个线程向仓库中生产产品。
public void produce(final int val) {
new Thread() {
public void run() {
depot.produce(val);
}
}.start();
}
} // 消费者
class Customer1 {
private Depot1 depot; public Customer1(Depot1 depot) {
this.depot = depot;
} // 消费产品:新建一个线程从仓库中消费产品。
public void consume(final int val) {
new Thread() {
public void run() {
depot.consume(val);
}
}.start();
}
} public class LockTest3 {
public static void main(String[] args) {
Depot1 mDepot = new Depot1(100);
Producer1 mPro = new Producer1(mDepot);
Customer1 mCus = new Customer1(mDepot); //5个线程共用同一把锁。2个线程减3个线程加。
mPro.produce(60);
mPro.produce(120);
mCus.consume(90);
mCus.consume(150);
mPro.produce(110);
}
}

多线程---ReentrantLock的更多相关文章

  1. 深入浅出多线程——ReentrantLock (二)

    深入浅出多线程——ReentrantLock (一)文章中介绍了该类的基本使用,以及在源码的角度分析lock().unlock()方法.这次打算在此基础上介绍另一个极为重要的方法newConditio ...

  2. Java多线程——ReentrantLock源码阅读

    上一章<AQS源码阅读>讲了AQS框架,这次讲讲它的应用类(注意不是子类实现,待会细讲). ReentrantLock,顾名思义重入锁,但什么是重入,这个锁到底是怎样的,我们来看看类的注解 ...

  3. java多线程 ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  4. 深入浅出多线程——ReentrantLock (一)

    ReentrantLock是一个排它重入锁,与synchronized关键字语意类似,但比其功能更为强大.该类位于java.util.concurrent.locks包下,是Lock接口的实现类.基本 ...

  5. java多线程---ReentrantLock源码分析

    ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...

  6. java多线程(三)线程的安全问题

    1.1. 什么是线程安全 如果有多个线程同时运行同一个实现了Runnable接口的类,程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的:反之,则是线程不 ...

  7. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  8. 高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile

    系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized.ReentrantLock.volatile. 一.synchronized 互斥锁,即操作互斥,并发线程过来, ...

  9. Java多线程的~~~Lock接口和ReentrantLock使用

    在多线程开发.除了synchronized这个keyword外,我们还通过Lock接口来实现这样的效果.由Lock接口来实现 这样的多线程加锁效果的优点是非常的灵活,我们不在须要对整个函数加锁,并且能 ...

随机推荐

  1. Win7去掉桌面图标小箭头

    去掉win7的快捷方式的小箭头: 每当我们装完一个软件,在桌面生成快捷方式的时候总会有个小箭头,有些朋友看到觉得很烦,如何去掉这个小箭头呢? 点击开始图标 - 附件 - 命令提示符(有情提示,请右击用 ...

  2. Redis的启动过程

    本文主要介绍Redis启动加载过程,总体上可以分为如下几步: 1. 初始化全局服务器配置 2. 加载配置文件(如果指定了配置文件,否则使用默认配置) 3. 初始化服务器 4. 加载数据库 5. 网络监 ...

  3. django model field validator 设置

    转自:http://blog.csdn.net/cwjcwj520/article/details/17330845 例子为想在创建用户名的时候验证输入字符是否有效,并且插入了even_field为C ...

  4. mySql的普通索引和复合索引

    有关普通索引和组合索引问题: 索引分单列索引和组合索引:单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引:组合索引,即一个索包含多个列.   MySQL索引类型包括:   ...

  5. sublime 使用总结

    不管你用什么编辑,sublime是首选编辑器,就是sublime淘汰,但已成为标准.例如:atom,几乎等同于sublime,及其他可以几乎调成到sublime操作方式. 一.常用插件 插件搜索地址: ...

  6. qt——QT中QWidget、QDialog及QMainWindow的区别

    QWidget类是所有用户界面对象的基类. 窗口部件是用户界面的一个基本单元:它从窗口系统接收鼠标.键盘和其它事件,并且在屏幕上绘制自己.每一个窗口部件都是矩形的,并且它们按Z轴顺序排列.一个窗口部件 ...

  7. li设置float后ul无法包裹li问题解决

    解决办法:灰常简单,只需给ul添加样式 ul{ overflow: auto; } 即可

  8. 10 ref 和 out 之间的差别

    (1) 两者都是按地址传递的,使用后都将改变原来的数值 (2) ref传进去的參数必须在调用前初始化,out不必 (3) ref传进去的參数在函数内部能够直接使用,而out不可 (4) ref传进去的 ...

  9. Python3学习之路~2.3 字符串操作

    字符串操作 特性:不可修改 name="my \tname is alex" print(name.capitalize()) #首字母变大写 print('Alex LI'.ca ...

  10. 图结构练习——判断给定图是否存在合法拓扑序列(sdutoj)

    #include<stdio.h>#include<string.h>int d[15],map[15][15],vis[15];int main(){    int i,j, ...