zoukankan      html  css  js  c++  java
  • Java---Condition控制线程通信

    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

  • 相关阅读:
    Android之ToolBar的使用
    Android之 RecyclerView,CardView 详解和相对应的上拉刷新下拉加载
    Andorid 之日历控件,可左右滑动,包含公历,农历,节假日等
    Docker技术入门与实战 第二版-学习笔记-4-Dockerfile外其他生成镜像的方法
    Docker技术入门与实战 第二版-学习笔记-3-Dockerfile 指令详解
    Docker技术入门与实战 第二版-学习笔记-2-镜像构建
    Docker技术入门与实战 第二版-学习笔记-1-镜像
    docker官方文档学习-1-Docker for mac安装配置
    vagrant up下载box慢的解决办法
    主机ping不通virtualbox虚拟机的解决办法
  • 原文地址:https://www.cnblogs.com/jycboy/p/5623210.html
Copyright © 2011-2022 走看看