Java---Condition控制线程通信

时间:2022-12-19 08:14:32

java中控制线程通信的方法有:1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信。不灵活。

2.利用Condition控制线程通信,灵活。

3.利用管道pipe进行线程通信,不推荐

4.利用BlockingQueue控制线程通信

本文就讲解利用Condition控制线程通信,非常灵活的方式。

Condition类是用来保持Lock对象的协调调用。

对Lock不了解的可以看我的上一篇博客:http://www.cnblogs.com/jycboy/p/5623113.html

Condition介绍

使用Condition可以让那些已经得到lock对象却无法继续执行的线程释放lock对象,Condition对象也可以唤醒处于等待的线程。

ConditionObject 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,使用其 newCondition() 方法。

Condition类提供了如下三个方法:

await():造成当前线程在接到信号或被中断之前一直处于等待状态。 该方法流程:1.新建Condition Node包装线程,加入Condition队列。

2.释放当前线程占有的锁

3.阻塞当前线程

signal():唤醒当前lock对象的一个等待线程。signal方法只是将Node(await方法封装的)修改了状态,并没有唤醒线程。要将修改状态后的Node唤醒,一种是再次调用await(),一种是调用unlock()。//这局句很重要,不明白的可以看我下一篇博客。

signalAll():唤醒当前lock对象的所有等待线程。只有当前线程放弃对lock的锁定,被唤醒的线程才可以执行。

代码实例:

代码逻辑:Account类实现同步的取钱(draw)、存钱(deposit)操作;DrawThread循环取钱的线程、DepositThread循环存钱的线程。

Account:

package condition;

import java.util.concurrent.locks.*;
/**
*存钱、取钱
*/
public class Account
{
//显示定义Lock对象
private final Lock lock = new ReentrantLock();//可重入锁
//获得指定Lock对象对应的条件变量
private final Condition cond = lock.newCondition(); //获得condition实例 private String accountNo;
private double balance; //标识账户中是否已经存款的旗标
private boolean flag = false; public Account(){} public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
} public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
} public double getBalance()
{
return this.balance;
}
/**
*取款
* @param drawAmount
*/
public void draw(double drawAmount)
{
//加锁
lock.lock();
System.out.println(Thread.currentThread().getName() +"进入*区。。。。。。。。");
try
{
//如果账户中还没有存入存款,该线程等待
if (!flag)
{
cond.await();
}
else
{
//执行取钱操作
System.out.println(Thread.currentThread().getName() +
" 取钱:" + drawAmount);
balance -= drawAmount;
System.out.println("账户余额为:" + balance);
//将标识是否成功存入存款的旗标设为false
flag = false;
//唤醒该Lock对象对应的其他线程
cond.signalAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println("释放了");
}
}
/**
* 存款
* @param depositAmount
*/
public void deposit(double depositAmount)
{
lock.lock();
System.out.println(Thread.currentThread().getName() +"进入*区。。。。。。。。");
try
{
//如果账户中已经存入了存款,该线程等待
if(flag)
{
System.out.println(Thread.currentThread().getName() +"等待。。。。。。");
cond.await(); }
else
{
//执行存款操作
System.out.println(Thread.currentThread().getName() +
" 存款:" + depositAmount);
balance += depositAmount;
System.out.println("账户余额为:" + balance);
//将标识是否成功存入存款的旗标设为true
flag = true;
//唤醒该Lock对象对应的其他线程
cond.signalAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println(Thread.currentThread().getName() +"释放锁。。。。");
}
} public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if (obj != null && obj.getClass() == Account.class)
{
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}

  DrawThread:

package condition;

/**
*取钱
*/ public class DrawThread extends Thread
{
//模拟用户账户
private Account account;
//当前取钱线程所希望取的钱数
private double drawAmount; public DrawThread(String name , Account account ,
double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
} //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
public void run()
{
for (int i = 0 ; i < 6 ; i++ )
{
account.draw(drawAmount);
}
}
}

  DepositThread:

package condition;

/**
*存钱
*/ public class DepositThread extends Thread
{
//模拟用户账户
private Account account;
//当前取钱线程所希望取的钱数
private double depositAmount; public DepositThread(String name , Account account ,
double depositAmount)
{
super(name);
this.account = account;
this.depositAmount = depositAmount;
} //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
public void run()
{
for (int i = 0 ; i < 2 ; i++ )
{
account.deposit(depositAmount);
System.out.println(Thread.currentThread().getName()+" 存钱结束!");
}
}
}

  TestDraw:

package condition;

public class TestDraw
{
public static void main(String[] args)
{
//创建一个账户
Account acct = new Account("1234567" , 0);
new DrawThread("取钱者" , acct , 800).start();
new DepositThread("存钱者甲" , acct , 800).start();
new DepositThread("存钱者乙" , acct , 800).start();
new DepositThread("存钱者丙" , acct , 800).start();
}
}

  运行结果:

取钱者进入*区。。。。。。。。
存钱者甲进入*区。。。。。。。。
存钱者甲 存款:800.0
账户余额为:800.0
存钱者甲释放锁。。。。
存钱者丙进入*区。。。。。。。。
存钱者甲 存钱结束!
存钱者丙等待。。。。。。
存钱者乙进入*区。。。。。。。。
存钱者乙等待。。。。。。
释放了
存钱者甲进入*区。。。。。。。。
存钱者甲等待。。。。。。
取钱者进入*区。。。。。。。。
取钱者 取钱:800.0
账户余额为:0.0
释放了
取钱者进入*区。。。。。。。。

这里结果只粘贴了一部分。。。。聪明的你会发现这个程序最后阻塞啦,注意是阻塞不是死锁!阻塞的原因是:三个存钱的线程都运行结束了,但是取钱的线程还没有,所以阻塞啦。

对于await(),signal()方法流程不了解的可以看这篇博客:http://www.cnblogs.com/jycboy/p/5623238.html

转发请注明出处:http://www.cnblogs.com/jycboy/p/5623210.html

Java---Condition控制线程通信的更多相关文章

  1. Condition控制线程通信

    Condition控制线程通信 一.前言 java.util.concurrent.locks.Condition 接口描述了可能会与锁有关联的条件变量.这些变量在用法上与使用Object.wait ...

  2. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-&gt ...

  3. java多线程 -- Condition 控制线程通信

    Api文档如此定义: Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对 ...

  4. 10&period; Condition 控制线程通信

    1. 是什么 ? 2. 示例 package com.gf.demo09; import java.util.concurrent.locks.Condition; import java.util. ...

  5. Java并发包——线程通信

    Java并发包——线程通信 摘要:本文主要学习了Java并发包里有关线程通信的一些知识. 部分内容来自以下博客: https://www.cnblogs.com/skywang12345/p/3496 ...

  6. Java并发基础--线程通信

    java中实现线程通信的四种方式 1.synchronized同步 多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信.如下: public class S ...

  7. Java -- 使用阻塞队列(BlockingQueue)控制线程通信

    BlockingQueeu接口是Queue的子接口,但是它的主要作用并不是作为容器,而是作为线程同步的工具. 特征: 当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程 ...

  8. java笔记--关于线程通信

    关于线程通信 使用多线程编程的一个重要原因就是线程间通信的代价比较小 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897773.h ...

  9. Java基础教程——线程通信

    线程通信:等待.唤醒 Object方法 这些方法在拥有资源时才能调用 notify 唤醒某个线程.唤醒后不是立马执行,而是等CPU分配 wait 等待,释放锁,不占用CPU资源 notifyAll 唤 ...

随机推荐

  1. fireworks将图片变为透明色

    如果是新建的图片,只要把画布背景设置成透明,图片完成后保存为GIF格式即可: 如果是已经存在的图片,用Fireworks将图片打开,然后按Ctrl+Shift+X,在弹出界面中格式选择为GIF.在右边 ...

  2. &lpar;转&rpar;struts2&period;0配置文件、常量配置详解

    一.配置: 在struts2中配置常量的方式有三种: 在struts.xml文件中配置 在web.xml文件中配置 在sturts.propreties文件中配置 1.之所以使用struts.prop ...

  3. &lbrack;iOS基础控件 - 5&period;4&rsqb; 广告分页代码&lpar;UIScrollView制作&rpar;

    A.概念 例子就是桌面的APP列表,当APP数量超过一个屏幕,自动进行分页   B.实现思路 1.创建一个UIScrollView,这里设置为宽度跟屏幕相同,高度1/4屏幕高度左右 2.使用代码在UI ...

  4. c语言中3n&plus;1溢出问题解决

    3n+1问题是一个简单有趣而又没有解决的数学问题.这个问题是由L. Collatz在1937年提出的.克拉兹问题(Collatz problem)也被叫做hailstone问题.3n+1问题.Hass ...

  5. Asp&period;NET开启一个线程,不停的执行

    using System;using System.Threading;using System.Threading.Tasks; class StartNewDemo{    static void ...

  6. 基于maven创建和部署Webx项目

    1.准备工作 下载 Webx Maven 项目的目录结构Artifact插件. archetype-webx-quickstart-1.0.tar.gz插件:http://central.maven. ...

  7. Spring Cloud项目中通过Feign进行内部服务调用发生401&bsol;407错误无返回信息的问题

    问题描述 最近在使用Spring Cloud改造现有服务的工作中,在内部服务的调用方式上选择了Feign组件,由于服务与服务之间有权限控制,发现通过Feign来进行调用时如果发生了401.407错误时 ...

  8. 伪触发 input file 的click事件

    前端在做 input file 美化的时候,通常 把 input 定位position 到 已美化的按钮最上方 opacity: 0 HTML5时代,已有更方便的方法,点击美化按钮直接触发选择文件事件 ...

  9. shell中exec命令

    1.find中的-exec参数 在当前目录下(包含子目录),查找所有txt文件并找出含有字符串"bin"的行 find ./ -name "*.txt" -ex ...

  10. Awk使用及站点日志分析

    Awk使用及站点日志分析 Awk简单介绍 概述 awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入, ...