`
stone2oo6
  • 浏览: 26100 次
社区版块
存档分类
最新评论

线程笔记(wait,notify,notifyAll)

阅读更多

我们先看看代码片段,有点类似生产者-消费者模型中的生产者的逻辑代码。

  public synchronized void push(Object obj){  
        while( condition ){  
            try{  
                this.wait(); //等待,直到有数据出栈  
            }catch(InterruptedException e){  
            }  
        } 
        //do something
        stack.push(obj);  
        this.notify(); //通知其它线程把数据出栈  
    }
注意第4行,代码执行到这里会发生什么状态?
push方法的签名中有synchronized修饰,所以此方法具有排他性,锁(资源)为自身实例对象,也就是说同时同一个对象的这个方法只能被一个线程所执行,那么当执行到第4行时会发生什么呢? XX一紧,线程在释放排他锁资源之前,自己把自己给阻塞了? 这样岂不是死锁了?
翻翻JDK1.6手册,便恍然大悟:
wait方法共有3个重载函数:
  1. public final void wait(long timeout) throws InterruptedException
  2. public final void wait(long timeout, int nanos) throws InterruptedException
  3. public final void wait() throws InterruptedException
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:
  • 其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。
  • 其他某个线程调用此对象的 notifyAll 方法。
  • 其他某个线程中断线程 T
  • 大约已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。
然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用 wait 方法时的情况。然后,线程 Twait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调用 wait 方法时的情况完全相同。

简而言之:
  1. wait方法的作用释放已持有的锁,并进入休眠状态;当被唤醒或是等待超时,当前的线程又会重新加入到锁等待队列中,一旦获取了对该对象的控制权,则会从原来的地方继续执行;如果线程被中断,则会抛出InterruptedException异常。
  2. notify方法唤醒等待队列中的第一个线程并把它移入锁申请队列中
  3. notifyAll方法唤醒等待队列中的所有的线程并把它们移入锁申请队列中
  4. wait,notify,notifyAll必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用范围内,也就是出现在用synchronized修饰的方法或代码块中.
  5. 在更多线程的情况下(e.g. 拿消费者/生产者模型来讲,可能存在多个消费者和多个生产者),线程被唤醒后依然需要对条件进行测试,如果不满足该条件,则继续等待。换句话说,等待应总是发生在循环中。如上述代码示例(2-7循环体)
附上完整的示例代码:
public class ThreadDemo {
	private int x = 0;

	public synchronized void add() {
		while (x >= 2) {
			try {
				this.wait();
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

		x++;
		System.out.println("add:" + x);
		this.notifyAll();
	}

	public synchronized void sub() {
		int y = 1;
		while (x < 2) {
			System.out.println("sub:while repeat = " + y);
			y++;
			try {
				this.wait(); //this.wait(1);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

		x--;
		System.out.println("sub:" + x);
		this.notifyAll();
	}

	public static void main(String[] args) throws Throwable {
		final ThreadDemo tt = new ThreadDemo();
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 100; i++) {
					tt.add();
					try {
						Thread.sleep((int) (Math.random() * 1000));
					} catch (InterruptedException e) {
					}
				}

			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 100; i++) {
					tt.sub();
					try {
						Thread.sleep((int) (Math.random() * 1000));
					} catch (InterruptedException e) {
					}
				}

			}
		}).start();
	}
}
  在代码第25行处,可以试着设置下等待超时时间,然后看看测试结果。
0
2
分享到:
评论

相关推荐

    java多线程笔记全手打

    通过阅读`多线程笔记.doc`和运行`threadDemo`示例代码,你可以对Java多线程有更深入的理解,并能够在实际项目中灵活运用这些知识,解决并发问题。同时,博客地址提供了更多详细内容,可以帮助你进一步探索和实践。

    马士兵多线程训练营笔记

    5. **线程通信**:wait()、notify()、notifyAll()方法的使用,以及在生产者消费者模型、哲学家就餐问题等经典示例中的应用。 6. **线程池**:ExecutorService、ThreadPoolExecutor和Future接口的理解,线程池的配置...

    java线程笔记.docx

    `wait()`使线程等待直到其他线程调用`notify()`或`notifyAll()`唤醒它;`notify()`唤醒一个等待的线程;`notifyAll()`唤醒所有等待的线程。 #### 七、线程间协作 - **join()方法**:当前线程等待另一个线程执行...

    Java多线程详解(超详细)_狂神说笔记完整版_项目代码_适合小白随课程学习

    - `wait()`, `notify()`和`notifyAll()`方法用于线程间通信,需在同步环境中使用。 5. **线程通信问题** - 生产者-消费者问题:一个线程生产数据,另一个线程消费数据,需要避免生产者过度生产或消费者提前消费。...

    多线程笔记.docx

    此外,线程间的协作(如wait()、notify()和notifyAll())也是多线程编程中需要掌握的关键技巧。 总之,Java多线程技术是提升程序并发性能的重要手段,理解线程的基本概念、实现方式以及线程间的交互机制对于开发...

    java线程学习笔记

    3. Wait() 方法:使线程进入等待状态,释放锁资源,notify() 或 notifyAll() 唤醒线程,这三个方法必须在获得对象锁的状态下使用,否则报错。 4. IO 阻塞:线程因等待 IO 操作的完成而被阻塞。 线程优先级: 线程...

    JAVA 多线程学习笔记

    1. wait()、notify()和notifyAll():这三个方法用于线程间的通信,但必须在同步环境中使用,否则抛出`IllegalMonitorStateException`。 2. `BlockingQueue`:队列实现的线程通信,如`ArrayBlockingQueue`、`...

    多线程笔记_java/多线程_

    wait()、notify()和notifyAll()方法用于线程间的通信,这些方法必须在同步块或同步方法中使用,以避免唤醒错误的线程。 3.3 高级同步工具 Java提供了一些高级同步工具,如Semaphore(信号量)、CyclicBarrier(循环...

    多线程笔记2

    为了避免多线程间的资源竞争问题,Java提供了同步机制,如`synchronized`关键字、`wait()`, `notify()`, `notifyAll()`方法以及`Lock`接口。此外,`java.util.concurrent`包提供了高级并发工具,如`Semaphore`, `...

    狂神说多线程详解.rar

    4. `wait()`, `notify()`和`notifyAll()`:用于线程间的协作,必须在同步块中调用。 五、多线程的优点与挑战 1. 优点:提高系统资源利用率,提高并发性能,增强用户体验。 2. 挑战:线程安全问题、死锁和活锁风险、...

    java多线程代码笔记

    这些方法用于线程间通信,当一个线程调用`wait()`,它会释放对象的锁并等待,直到其他线程调用`notify()`或`notifyAll()`唤醒它。 Java的`java.util.concurrent`包提供了更高级的并发工具,如`Semaphore`(信号量)...

    Java多线程自学笔记

    - 使用`wait()`、`notify()`和`notifyAll()`等方法可以在线程之间传递控制权。 #### 六、线程安全性和并发控制 - **线程安全性**: - 确保在多线程环境下数据的一致性。 - 通过使用线程安全的数据结构或同步...

    java线程笔记

    线程间的通信可以通过wait()、notify()和notifyAll()方法实现,这些方法都定义在Object类中。但是,这些方法必须在同步控制块或方法中使用,否则会抛出IllegalMonitorStateException。此外,使用java.util....

    线程学习实例和笔记

    5. **线程通信**:线程间的通信可以通过wait(), notify()和notifyAll()方法,或者使用高级API如Semaphore、BlockingQueue等实现。 6. **线程池**:线程池可以有效地管理和控制线程,避免频繁创建和销毁线程带来的...

    java多线程编程笔记

    学习Java多线程编程不仅涉及到线程的创建和启动,还包括线程同步(如`synchronized`关键字、`wait()`, `notify()`, `notifyAll()`方法)、线程间通信(`BlockingQueue`、`Future`、`ExecutorService`)、线程池(`...

    Java多线程学习笔记

    - **notify()** 和 **notifyAll()**:唤醒一个或所有等待的线程。 #### 七、总结 本篇笔记主要介绍了Java中线程的基本概念、创建与启动、调度与优先级、状态控制以及线程同步等内容。这些基础概念和技术是Java并发...

    Java线程编程学习笔记(二)

    在描述中虽然没有具体信息,但提到的博客链接(https://metaphy.iteye.com/blog/406870)可能包含了更详细的讨论,包括线程同步和通信机制,比如synchronized关键字、wait()、notify()和notifyAll()方法,以及...

    多线程学习笔记.docx

    wait()、notify()和notifyAll()是Object类的方法,常用于实现生产者消费者模式,线程通过这些方法进行通信,等待或唤醒其他线程。 生产者消费者模式是一种典型的线程同步场景,其中生产者线程负责生成数据,消费者...

    笔记-1、线程基础、线程之间的共享和协作2

    线程间的协作常常借助于等待和通知机制,如`wait()`、`notify()`和`notifyAll()`。这些方法需要在同步块(即持有对象锁)内调用,以避免死锁。`wait()`使线程进入等待状态,直到被`notify()`或`notifyAll()`唤醒。...

Global site tag (gtag.js) - Google Analytics