如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能用wait()、notify()、notifyAll()方法进行线程通信了。当使用Lock对象来保证同步时,Java提供了Condition类来协调线程间的通信。

本示范简单模拟银行帐户的存取款活动,帐户余额大于等于取款金额时允许取款;帐户余额小于1000时允许存款(这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了)。

1. 实体Account类

package com.clzhang.sample.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; public class Account {
// 锁对象
private final Lock lock = new ReentrantLock();
// Condition对象
private final Condition condDeposit = lock.newCondition();
private final Condition condWithdraw = lock.newCondition(); // 为避免double类型计算的误差,balance类型设计为int的
private int balance; public Account(int balance) {
this.balance = balance;
} public void withdraw(int drawAmount) {
// 加锁
lock.lock();
try {
// 如果帐户余额不足,则取钱方法阻塞
while (balance < drawAmount)
condWithdraw.await(); // 执行取钱
balance -= drawAmount;
System.out.println(Thread.currentThread().getName() + " 取钱:" + drawAmount + "账户余额为:"
+ balance); // 唤醒存款线程
condDeposit.signal();
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
} // 按照目前设计,帐户余额大于1000后不让存款,必须先取款后才能再存。
// 这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了。
public void deposit(int depositAmount) {
lock.lock();
try {
// 如果帐户余额大于1000,存钱方法阻塞
while (balance > 1000)
condDeposit.await(); // 执行存款
balance += depositAmount;
System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount + "账户余额为:"
+ balance); // 唤醒取款线程
condWithdraw.signal();
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
}

2. 调用类(DepositDrawTest类)

package com.clzhang.sample.thread;

class DrawThread extends Thread {
// 模拟用户账户
private Account account;
// 每次取钱数
private int drawAmount; public DrawThread(String name, Account account, int drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
} @Override
public void run() {
for (int i = 0; i < 3; i++) {
account.withdraw(drawAmount);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
} class DepositThread extends Thread {
// 模拟用户账户
private Account account;
// 每次存钱数
private int depositAmount; public DepositThread(String name, Account account, int depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
} @Override
public void run() {
for (int i = 0; i < 3; i++) {
account.deposit(depositAmount);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
} public class DepositDrawTest { public static void main(String[] args) {
// 创建一个账户,初始帐户余额为0
Account acct = new Account(0); // 注意下面的取款与存款的balance参数值需要匹配,
// 否则可能造成存款过多而不让存,然后又没有人取款导致程序无法正常终止的问题。
new DrawThread("取钱者1", acct, 400).start();
new DrawThread("取钱者2", acct, 600).start();
new DepositThread("存款者甲", acct, 600).start();
new DepositThread("存款者乙", acct, 200).start();
new DepositThread("存款者丙", acct, 400).start();
}
}

输出:

存款者甲 存款:600账户余额为:600
存款者乙 存款:200账户余额为:800
存款者丙 存款:400账户余额为:1200
取钱者1 取钱:400账户余额为:800
取钱者2 取钱:600账户余额为:200
存款者乙 存款:200账户余额为:400
存款者甲 存款:600账户余额为:1000
取钱者2 取钱:600账户余额为:400
存款者丙 存款:400账户余额为:800
取钱者1 取钱:400账户余额为:400
存款者甲 存款:600账户余额为:1000
存款者丙 存款:400账户余额为:1400
取钱者1 取钱:400账户余额为:1000
存款者乙 存款:200账户余额为:1200
取钱者2 取钱:600账户余额为:600

3. 总结

  1. 如果取款金额大于余额则不让取款,等存款队列继续存钱,余额足够支付时再让取款。
  2. 如果存款过多(大于1000),则存款不让存了,等取款队列把钱取走,余额降低到1000以下时,可以继续存款。
  3. 这样就允许多次连续取款(只要帐户有钱),多次连续存款(余额不能大于1000),而不是存款、取款依次调用。

Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信的更多相关文章

  1. 《java多线程编程核心技术》不使用等待通知机制 实现线程间通信的 疑问分析

    不使用等待通知机制 实现线程间通信的 疑问分析 2018年04月03日 17:15:08       ayf 阅读数:33 编辑 <java多线程编程核心技术>一书第三章开头,有如下案例: ...

  2. 《JAVA多线程编程核心技术》 笔记:第三章:线程间通信

    一. 等待/通知机制:wait()和notify()1.1.使用的原因:1.2 具体实现:wait()和notify()1.2.1 方法wait():1.2.2 方法notify():1.2.3 wa ...

  3. 扯扯python的多线程的同步锁 Lock RLock Semaphore Event Condition

    我想大家都知道python的gil限制,记得刚玩python那会,知道了有pypy和Cpython这样的解释器,当时听说是很猛,也就意味肯定是突破了gil的限制,最后经过多方面测试才知道,还是那德行… ...

  4. Windows环境下多线程编程原理与应用读书笔记(4)————线程间通信概述

    <一>线程间通信方法 全局变量方式:进程中的线程共享全局变量,可以通过全局变量进行线程间通信. 参数传递法:主线程创建子线程并让子线程为其服务,因此主线程和其他线程可以通过参数传递进行通信 ...

  5. java并发编程(十)使用wait/notify/notifyAll实现线程间通信

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17225469 wait()方法:public final void wait()  thr ...

  6. Java多线程编程(同步、死锁、生产消费者问题)

    Java多线程编程(同步.死锁.生产消费): 关于线程同步以及死锁问题: 线程同步概念:是指若干个线程对象并行进行资源的访问时实现的资源处理保护操作: 线程死锁概念:是指两个线程都在等待对方先完成,造 ...

  7. java线程间通信1--简单实例

    线程通信 一.线程间通信的条件 1.两个以上的线程访问同一块内存 2.线程同步,关键字 synchronized 二.线程间通信主要涉及的方法 wait(); ----> 用于阻塞进程 noti ...

  8. python笔记9 线程进程 threading多线程模块 GIL锁 multiprocessing多进程模块 同步锁Lock 队列queue IO模型

    线程与进程 进程 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写的程序用来描述进程要完成哪些功能以及如何完成:数据集则是程序在执行过程中所需要 ...

  9. python 多线程中的同步锁 Lock Rlock Semaphore Event Conditio

    摘要:在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lo ...

随机推荐

  1. Struts+Hibernate+jsp页面 实现分页

    dao层数据库代码: package com.hanqi.dao; import java.util.ArrayList; import java.util.List; import org.hibe ...

  2. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  3. 虚拟机安装Centos64位Basic Service后 ifconfig查看无ip

    vi /etc/sysconfig/network-scripts/ifcfg-eth0 将 ONBOOT="no" 改为 ONBOOT="yes" 保存后: ...

  4. Ext.net 异常统一管理,铥掉可恶的 Request Failure

    Ext.net 异常统一管理,铥掉可恶的 Request Failure 看着这样的框框是不是很不爽 灭他.也不难.. .如果全部页面都有继承一个自定义的父类 ..那整个项目代码量就只有几行了.. 单 ...

  5. HDU-4518 吉哥系列故事——最终数 AC自动机+数位DP

    题意:如果一个数中的某一段是长度大于2的菲波那契数,那么这个数就被定义为F数,前几个F数是13,21,34,55......将这些数字进行编号,a1 = 13, a2 = 21.现给定一个数n,输出和 ...

  6. iOS - PassData

    前言 页面传值: 复合传值 单例传值 userDefaults 传值 代理传值 Block/闭包传值 1.复合传值 复合 - 正向传值 1.接收方,头文件中创建可存放传递值的属性变量 2.发送方,包含 ...

  7. php使用cURL实现Get和Post请求的方法

    1.cURL介绍 cURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP.FTP.TELNET等.最爽的是,PHP也支持 cURL 库.本文将介绍 cURL 的一些高级特性 ...

  8. getAttribute()方法的使用小笔记

    今天在某课网上复习js,突然卡在一个小例子上.反复做了几遍就是报错.终于查询w3c才找到错误的源头. 这里记录一下,谨防以后又忘了. 错误码: <body> <p id=" ...

  9. (五)Super VLAN

  10. Linux socket编程应用学习笔记

    参考这个系列吧 http://www.cnblogs.com/wunaozai/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/default.html?page=2 ...