Java多线程之线程的通信

Java多线程之线程的通信

在总结多线程通信前先介绍一个概念:锁池。线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池。每个对象都有自己的锁池的空间,用于放置等待运行的线程。这些线程中哪个线程拿到锁标记由系统决定。前面我们也有T到死锁的概念,线程互相等待其他线程释放锁标记,而又不释放自己的;造成无休止地等待。当出现死锁的时候,我们应该如何解决呢?通过线程间的通信解决。

  • 线程间通信:

多线程之间的通信有2种方式,第一种是使用object类的几个方法,第二种是使用条件变了来控制协调。这里重点介绍第一种

1,线程间通信机制实际上也就是协调机制。线程间通信使用的空间称之为对象的等待队列,这个队列也属于对象的空间。值得         注意的是,一个对象除了由属性和方法之外还有互斥锁标记、锁池空间和等待队列空间。

2,wait()方法:导致当前线程等待

3,Notify()方法:唤醒在此同步监视器上面的一个线程。

4,NotifyAll()方法:唤醒在此同步监视器上面的所有线程。

  •     值得注意的是:

1,只能对加锁的资源(Synchronized方法里)进行wait()和notify()。

2,我们应该用notifyall取代notify,因为我们用notify释放出的一个线程是不确定的,由OS决定。

3,释放锁标记只有在Synchronized 代码结束或者调用wait()。

4,锁标记是自己不会自动释放,必须有通知。

5,在程序中判定一个条件是否成立时要注意使用while要比使用if更严密。

下面代码模拟了一个存款和取款的逻辑,具体代码如下:

/**
 *
 * @version 1L
 * @author  LinkinPark
 * @since   2015-2-5
 * @motto   梦似烟花心似水,同学少年不言情
 * @desc    ^  以后在编码中养成良好的习惯,要是一个类的某些属性不能随便更改的话,就不要提供set方法
 */
public class Account
{
	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;
	}

	// 因此账户余额不允许随便修改,所以只为balance提供getter方法,不要提供set方法
	public double getBalance()
	{
		return this.balance;
	}

	public synchronized void draw(double drawAmount)
	{
		try
		{
			// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
			if (!flag)
			{
				wait();
			}
			else
			{
				// 执行取钱
				System.out.println(Thread.currentThread().getName() + " 取钱:" + drawAmount);
				balance -= drawAmount;
				System.out.println("账户余额为:" + balance);
				// 将标识账户是否已有存款的旗标设为false。
				flag = false;
				// 唤醒其他线程
				notifyAll();
			}
		}
		catch (InterruptedException ex)
		{
			ex.printStackTrace();
		}
	}

	public synchronized void deposit(double depositAmount)
	{
		try
		{
			// 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
			if (flag)
			{
				wait();
			}
			else
			{
				// 执行存款
				System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount);
				balance += depositAmount;
				System.out.println("账户余额为:" + balance);
				// 将表示账户是否已有存款的旗标设为true
				flag = true;
				// 唤醒其他线程
				notifyAll();
			}
		}
		catch (InterruptedException ex)
		{
			ex.printStackTrace();
		}
	}

	// 下面两个方法根据accountNo来重写hashCode()和equals()方法
	public int hashCode()
	{
		return accountNo.hashCode();
	}

	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj != null && obj.getClass() == Account.class)
		{
			Account target = (Account) obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}
/**
 *
 * @version 1L
 * @author LinkinPark
 * @since 2015-2-5
 * @motto 梦似烟花心似水,同学少年不言情
 * @desc ^  取钱的线程
 */
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;
	}

	// 重复100次执行取钱操作
	public void run()
	{
		for (int i = 0; i < 100; i++)
		{
			account.draw(drawAmount);
		}
	}
}
/**
 * @version 1L
 * @author LinkinPark
 * @since 2015-2-5
 * @motto 梦似烟花心似水,同学少年不言情
 * @desc ^存钱的线程
 */
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;
	}

	// 重复100次执行存款操作
	public void run()
	{
		for (int i = 0; i < 100; i++)
		{
			account.deposit(depositAmount);
		}
	}
}
public class DrawTest
{
	public static void main(String[] args)
	{
		// 创建一个账户
		Account acct = new Account("NightWish", 0);
		new DrawThread("取钱者", acct, 800).start();
		new DepositThread("存款者甲", acct, 800).start();
		new DepositThread("存款者乙", acct, 800).start();
		new DepositThread("存款者丙", acct, 800).start();
	}
}

关于第2种方式,了解下就好了。我翻了下API,上面是这样子介绍Condition的。Condition将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 这种方式和前面讲的lock锁相互使用,lock一般使用ReentrantLock做实现类,有lock和unlock2个加锁和释放锁的方法,这里Condition由lock的newCondition()方法获得,然后有await,signal,signalAll3个方法比较简单的,这里不做赘述了。

时间: 2024-11-07 17:39:56

Java多线程之线程的通信的相关文章

Java多线程 二 线程间通信

线程间通信: 多个线程在处理同一资源,但是 等待唤醒机制 涉及的方法: 1.wait() 让线程处于冻结状态,被wait的线程会被存储到线程池中. 2.notify() 唤醒线程池中的一个线程(任意) 3.notifyAll() 唤醒线程池中的所有线程.. 这些方法都必须定义在同步中, 因为这些方法是用于操作线程状态的方法. 必须明确到底操作的那个锁上的线程. 为什么操作线程的方法wait notify notifyAll定义在了Object中. 因为这些方法是监视器方法,监视器其实就是锁. 锁

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例

JAVA多线程之线程间的通信方式

一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信. 参考示例: public class MyObject { synchronized public void methodA() { //do something.... } synchronized public void methodB()

Java多线程之线程结束清理

该事例说明了清理工作必须要放在finally块中 package Thread.Interrupting; import java.util.concurrent.TimeUnit; class NeedsCleanup { private final int id; public NeedsCleanup(int ident) { id = ident; System.out.println("NeedsCleanup " + id); } public void cleanup()

Java多线程之线程中断

该例子说明,Sleep可以被中断,但是I/O和synchronized不能被中断. package Thread.Interrupting; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; imp

java多线程之线程的同步与锁定(转)

一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. publicclass Foo { privateint x = 100; publicint getX() { return x;     } publicint fix(int y) {         x = x - y; return x;     } } publicclass MyRunnable i

java多线程之 ---- 线程死锁

java多线程之线程死锁 产生死锁的主要原因: 因为系统资源不足. 进程运行推进的顺序不合适. 资源分配不当等. 如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁.其次, 进程运行推进顺序与速度不同,也可能产生死锁. 产生死锁的四个必要条件:  互斥条件:一个资源每次只能被一个进程使用. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺. 循环等待条件:若干进

java多线程之 ---- 线程同步

java多线程之线程同步 线程同步 定义:同步是指在同一时间段内只能运行一个线程. 分类:同步方法.同步块. 作用:安全解决共享问题. 同步块: 语法: synchronized (同步对象) { 需要同步的代码; } 例子: public class ThreadDemo implements Runnable{ private int ticket = 5; public void run(){ for(int i=1;i<=5;i++){ synchronized (this){ if(t

Java多线程之线程的同步

Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同一个数据时,非常容易出现线程安全问题.具体原因如下:   1,多个线程同时访问一个数据资源(该资源称为临界资源),形成数据发生不一致和不完整.   2,数据的不一致往往是因为一个线程中的多个关联的操作(这几个操作合成原子操作)未全部完成. 关于线程安全问题,有一个经典的情景:银行取钱.代码如下: /