线程——线程间的通信——wait()的区别 和sleep()

本文通过存款取款的例子,详细介绍了Java中线程间的通信机制,包括如何使用synchronized关键字来实现线程安全,以及wait()和notify()方法在同步上下文中的应用。
线程间的通信:


在多个线程操作同一个对象的时候,cup在处理多个线程的时候会出现一个线程还没有完全处理完 另一个线程抢夺资源,导致意想不到的结果发生所以此时用synchronized锁 会防止不安全因素的发生。

package hr.csdn.com;

public class BlankTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub\
		 Blank m=new Blank();
		 new Thread(new saving(m)).start();
		 new Thread(new takemoney(m)).start();
	}
}
class Blank{
	String name="张三";//定义成员变量
	int money=100;//存入的金额
	int qumoney=0;//取出的金额
	
	boolean flg=false;//作为调用wait()方法的一个标志
	public synchronized void cunqian(String name,int money,int qumoney){//同步函数,可以有效的防止因为资源释放而导致我们不想得到的结果
		if(flg){
			try {
				wait();//此方法可以另当前线程处于睡眠状态,让另一个线程继续按照我们的意向进行
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name=name;
		this.money=money;
		this.qumoney=qumoney;
		try {
			Thread.sleep(500);//让当前线程短暂休眠,为了更清楚的看到运行结果
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(name+" "+"存入"+" "+money);
		flg=true;//将标记设为true 使线程执行wait()方法
		notify();//唤醒刚刚处于wait()状态的线程,使其继续执行。在这里,如果cunqian(……)这个线程处于处于执行阶段,在它要到wait()这个
		//阶段之前,一定要唤醒 quqian()这个线程。切记 这里的notify()不是唤醒的cunqian(……)这个线程
	}
	public synchronized void quqian(){
		int morethan=money-qumoney;//余额设置变量
		if(!flg){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(qumoney<=money){
			System.out.println(name+" "+"取出"+" "+qumoney+"余额"+morethan);
		}else{
			System.out.print("您已透支");
		}
		try {
			Thread.sleep(500);//同上
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		flg=false;
		notify();//这里notify()也是 如果此时quqian(……)这个线程处于执行阶段,那么在次之前一定要唤醒cunqian(……)这个线程
	}
	
	
}
class saving implements Runnable{
	Blank b=null;//设置被操作类的对象为null
	public saving(Blank b){
		this.b=b;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
			while(true){
				b.cunqian("张三",100,20);
			}
		
	}
		
}
class takemoney implements Runnable{
	Blank b=null;
	public takemoney(Blank b){
		this.b=b;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			b.quqian();
		}
		
	}
	
}
新手在学习JAVA的时候一定要明白什么是资源:
比如在前几篇文章里有 一个卖票的例子,那么卖的票 也就是tickets=100 这就是资源。
多个线程在操作同一个对象的时候。那么就可以把cpu看做资源,因为只有抢到cup的线程才能继续执行并且去处理 这100张票的分配。(要知道我们所谓的同步执行,并不是多个线程在同时执行 而是cpu在以特别快的速度随机分配个任意一个线程时间片,只是这种速度特别快,使我们感觉好像在同意时间执行似的)。
所谓的释放资源 其实就是把cpu让出来,让其他的线程先执行。
wait()和sleep()的区别
wait() 是释放资源并且释放锁。
sleep() 释放资源但不释放锁。
举一个 日常生活的例子:
我们去厕所的时候,在厕所里面的哥们如果处于wait()状态 如果他不释放锁,也就是一直在厕所里呆着没有人交他,那么在厕所外面的哥们就得一直等着……但如果是处于sleep()状态就算没人叫他,一会儿就会醒,后面的哥们还是可以进去,所以sleep()不需要释放锁

Java线程编程中,`wait()` `notify()` 是用于实现线程间通信的核心机制之一。它们允许线程在特定条件下等待或被唤醒,从而实现协调执行,避免资源竞争不必要的CPU消耗。 ### 线程通信的基本原理 `wait()` 方法使当前线程进入等待状态,并释放对象锁,直到其他线程调用 `notify()` 或 `notifyAll()` 方法来唤醒它。`notify()` 方法则用于唤醒在该对象上等待的单个线程(`notifyAll()` 会唤醒所有等待线程),但它不会立即释放锁,只有在当前线程退出同步块或方法后,被唤醒的线程才有机会重新获取锁并继续执行。 这两个方法必须与 `synchronized` 关键字一起使用,确保线程在调用 `wait()` 或 `notify()` 时已经获得了对象锁。否则会抛出 `IllegalMonitorStateException` 异常。 ### 示例代码 以下是一个使用 `wait()` `notify()` 实现的简单线程间通信示例: ```java public class WaitNotifyExample { private final Object lock = new Object(); private boolean flag = false; public void waitForFlag() { synchronized (lock) { while (!flag) { try { System.out.println("Thread is waiting for flag to be true"); lock.wait(); // 释放锁并等待 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } System.out.println("Flag is true now, proceeding"); } } public void setFlagTrue() { synchronized (lock) { flag = true; System.out.println("Flag set to true, notifying waiting thread"); lock.notify(); // 唤醒等待的线程 } } public static void main(String[] args) { WaitNotifyExample example = new WaitNotifyExample(); Thread t1 = new Thread(example::waitForFlag); Thread t2 = new Thread(example::setFlagTrue); t1.start(); try { Thread.sleep(1000); // 模拟延迟 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } t2.start(); } } ``` ### 执行流程说明 1. **线程 t1** 调用 `waitForFlag()`,进入同步块并检查 `flag` 是否为 `false`。由于初始为 `false`,它调用 `wait()`,释放锁并进入等待状态。 2. **线程 t2** 调用 `setFlagTrue()`,将 `flag` 设置为 `true`,然后调用 `notify()` 唤醒在 `lock` 上等待的线程。 3. **线程 t1** 被唤醒后重新竞争锁,再次检查 `flag`,此时为 `true`,继续执行后续逻辑。 ### 注意事项 - **必须在同步上下文中调用**:`wait()` `notify()` 必须在 `synchronized` 方法或代码块中调用,以确保线程持有对象锁。 - **使用循环判断条件**:为了避免虚假唤醒(spurious wakeups),通常将 `wait()` 放在 `while` 循环中,确保条件真正满足后再继续执行。 - **避免死锁**:确保至少有一个线程可以改变条件并调用 `notify()` 或 `notifyAll()`,否则可能导致所有线程都处于等待状态。 - **异常处理**:调用 `wait()` `notify()` 可能抛出 `InterruptedException`,应妥善处理中断信号,避免线程挂起。 ### 优势与应用场景 相比轮询机制,`wait/notify` 能显著降低CPU资源的浪费。适用于多个线程之间需要根据某些状态进行协调的场景,例如生产者-消费者模型、任务调度、状态同步等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值