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. vim上下左右键输出A B

    (转)vim上下左右键不能用 把下面这段话存到~/.vimrc就可以了. " An example for a vimrc file. " " Maintainer: B ...

  2. RestTemplate异常no suitable HttpMessageConverter found for request type [java.lang.Integer]

    GET方式,参数必须放在URL后面,http://xxx/list?name={name}&age={age} package com.chelizi.xiruo.xframework.uti ...

  3. 关于keyGenerator,KeyPairGenerator,SecretKeyFactory的解析

    Java加密的常用的加密算法类型有三种 1单向加密:也就是不可逆的加密,例如MD5,SHA,HMAC 2对称加密:也就是加密方和解密方利用同一个秘钥对数据进行加密和解密,例如DES,PBE等等 3非对 ...

  4. POJ1797 Heavy Transpotation

    Background  Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand bu ...

  5. NLP常用语料集合

    常用语料资源 下面提供一些网上能下载到的中文的好语料,供研究人员学习使用.(1).中科院自动化所的中英文新闻语料库 http://www.datatang.com/data/13484中文新闻分类语料 ...

  6. a loosely strongly typed language

    JavaScript: The Definitive Guide, Sixth Edition by David Flanagan As explained above, the following ...

  7. Django实现微电影网站

    主要实现功能: 注册,登陆,播放视频,评论视频,xadmin实现后台提供视频管理,权限管理等等 首页(搜索,筛选) 搜索页面 播放页面 个人中心 后台 源码下载 环境配置:python2.7  pip ...

  8. Heavy Transportation---poj1797

    求(Dijkstra算法,求每条路径上的最小值 的最大值)和青蛙的那题类似:   #include<iostream> #include<stdio.h> #include&l ...

  9. grunt学习一

    grunt是前端自动化工具之一.下面是是grunt的简单小示例: 在使用grunt,确保安装nodejs,如果不清楚,可以百度找相关教程,这个教程已经烂大街了. 1.打开cmd,以管理员的身份.(或者 ...

  10. 【Python练习】文件引用用户名密码登录系统

    一.通过txt文件引入用户名密码 1 #coding=utf-8 from selenium import webdriver #from selenium.common.exceptions imp ...