Java线程:线程的交互
SCJP5学习笔记
线程交互是比较复杂的问题,SCJP要求不很基础:给定一个场景,编写代码来恰当使用等待、通知和通知所有线程。
一、线程交互的基础知识
SCJP所要求的线程交互知识点需要从java.lang.Object的类的三个方法来学习:
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
当然,wait()还有另外两个重载方法:
void wait(long timeout)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
以上这些方法是帮助线程传递线程关心的时间状态。
关于等待/通知,要记住的关键点是:
必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。
wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。
下面看个例子就明白了:
/**
* 计算输出其他线程锁计算的数据
*
* @author leizhimin 2008-9-15 13:20:38
*/
public class ThreadA {
public static void main(String[] args) {
ThreadB b = new ThreadB();
//启动计算线程
b.start();
//线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
synchronized (b) {
try {
System.out.println("等待对象b完成计算。。。");
//当前线程A等待
b.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b对象计算的总和是:" + b.total);
}
}
}
* 计算输出其他线程锁计算的数据
*
* @author leizhimin 2008-9-15 13:20:38
*/
public class ThreadA {
public static void main(String[] args) {
ThreadB b = new ThreadB();
//启动计算线程
b.start();
//线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
synchronized (b) {
try {
System.out.println("等待对象b完成计算。。。");
//当前线程A等待
b.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b对象计算的总和是:" + b.total);
}
}
}
/**
* 计算1+2+3 ... +100的和
*
* @author leizhimin 2008-9-15 13:20:49
*/
public class ThreadB extends Thread {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
//(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒
notify();
}
}
}
* 计算1+2+3 ... +100的和
*
* @author leizhimin 2008-9-15 13:20:49
*/
public class ThreadB extends Thread {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
//(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒
notify();
}
}
}
等待对象b完成计算。。。
b对象计算的总和是:5050
Process finished with exit code 0
b对象计算的总和是:5050
Process finished with exit code 0
千万注意:
当在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时,并不意味着这时线程会放弃其锁。如果线程荣然在完成同步代码,则线程在移出之前不会放弃锁。因此,只要调用notify()并不意味着这时该锁变得可用。
二、多个线程在等待一个对象锁时候使用notifyAll()
在多数情况下,最好通知等待某个对象的所有线程。如果这样做,可以在对象上使用notifyAll()让所有在此对象上等待的线程冲出等待区,返回到可运行状态。
下面给个例子:
/**
* 计算线程
*
* @author leizhimin 2008-9-20 11:15:46
*/
public class Calculator extends Thread {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
}
//通知所有在此对象上等待的线程
notifyAll();
}
}
* 计算线程
*
* @author leizhimin 2008-9-20 11:15:46
*/
public class Calculator extends Thread {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
}
//通知所有在此对象上等待的线程
notifyAll();
}
}
/**
* 获取计算结果并输出
*
* @author leizhimin 2008-9-20 11:15:22
*/
public class ReaderResult extends Thread {
Calculator c;
public ReaderResult(Calculator c) {
this.c = c;
}
public void run() {
synchronized (c) {
try {
System.out.println(Thread.currentThread() + "等待计算结果。。。");
c.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "计算结果为:" + c.total);
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
//启动三个线程,分别获取计算结果
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
//启动计算线程
calculator.start();
}
}
* 获取计算结果并输出
*
* @author leizhimin 2008-9-20 11:15:22
*/
public class ReaderResult extends Thread {
Calculator c;
public ReaderResult(Calculator c) {
this.c = c;
}
public void run() {
synchronized (c) {
try {
System.out.println(Thread.currentThread() + "等待计算结果。。。");
c.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "计算结果为:" + c.total);
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
//启动三个线程,分别获取计算结果
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
//启动计算线程
calculator.start();
}
}
运行结果:
Thread[Thread-1,5,main]等待计算结果。。。
Thread[Thread-2,5,main]等待计算结果。。。
Thread[Thread-3,5,main]等待计算结果。。。
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread not owner
at java.lang.Object.notifyAll(Native Method)
at threadtest.Calculator.run(Calculator.java:18)
Thread[Thread-1,5,main]计算结果为:5050
Thread[Thread-2,5,main]计算结果为:5050
Thread[Thread-3,5,main]计算结果为:5050
Process finished with exit code 0
Thread[Thread-2,5,main]等待计算结果。。。
Thread[Thread-3,5,main]等待计算结果。。。
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread not owner
at java.lang.Object.notifyAll(Native Method)
at threadtest.Calculator.run(Calculator.java:18)
Thread[Thread-1,5,main]计算结果为:5050
Thread[Thread-2,5,main]计算结果为:5050
Thread[Thread-3,5,main]计算结果为:5050
Process finished with exit code 0
运行结果表明,程序中有异常,并且多次运行结果可能有多种输出结果。这就是说明,这个多线程的交互程序还存在问题。究竟是出了什么问题,需要深入的分析和思考,下面将做具体分析。
实际上,上面这个代码中,我们期望的是读取结果的线程在计算线程调用notifyAll()之前等待即可。 但是,如果计算线程先执行,并在读取结果线程等待之前调用了notify()方法,那么又会发生什么呢?这种情况是可能发生的。因为无法保证线程的不同部分将按照什么顺序来执行。幸运的是当读取线程运行时,它只能马上进入等待状态----它没有做任何事情来检查等待的事件是否已经发生。 ----因此,如果计算线程已经调用了notifyAll()方法,那么它就不会再次调用notifyAll(),----并且等待的读取线程将永远保持等待。这当然是开发者所不愿意看到的问题。
因此,当等待的事件发生时,需要能够检查notifyAll()通知事件是否已经发生。
通常,解决上面问题的最佳方式是
本文出自 “熔 岩” 博客
相关推荐
Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的同步-同步方法 Java线程:...
Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:volatile关键字 Java线程:新特征-线程池 一、固定大小的线程池 二、单任务线程池 三、可变尺寸的线程池 四、延迟连接池 五、单任务延迟连接池 ...
#### 六、Java线程:线程的交互 线程之间的交互涉及到线程间的通信和同步问题。Java提供了`wait()`、`notify()`和`notifyAll()`等方法来实现线程间的等待和通知。 #### 七、Java线程:线程的调度 Java中的线程...
- **线程交互**:线程之间的通信机制,包括等待/通知机制,生产者-消费者模型等。 - **线程调度**:包括休眠、优先级调整、线程让步、合并线程以及守护线程的概念。 - **并发协作**:深入探讨了生产者-消费者模型和...
综上所述,Java多线程编程涉及到线程的概念、创建与启动、线程栈模型、线程状态转换、同步与锁、线程交互、调度策略、同步方法、并发协作模式、`volatile`关键字以及新特性等多个方面,掌握这些知识对于开发高性能、...
#### 六、Java线程的交互 - **线程通信**: - Java中的线程间通信主要包括 `wait()`、`notify()` 和 `notifyAll()` 方法。 - 这些方法用于实现线程间的等待-通知机制,通常在同步上下文中使用。 #### 七、Java...
这个压缩包中的文件提供了几个关于Java多线程交互的实例,可以帮助我们深入理解如何在并发环境中控制线程的同步,确保数据的一致性和安全性。 首先,让我们讨论一下标题和描述中提到的关键概念——“多线程交互”和...
Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...
创建和启动Java线程: * 可以使用extends Thread方法创建线程。 * 可以使用Runnable接口创建线程。 线程的状态和线程控制: * 线程的状态包括新建、可运行、阻塞、等待、死亡等。 * 线程控制包括线程的启动、暂停...
3. **线程交互**:线程之间的通信主要通过共享内存(共享变量)和消息传递(wait(), notify(), notifyAll())完成。线程间的交互需谨慎,避免出现竞态条件和死锁。 4. **线程合并与让步**:join()方法用于合并线程...
2. **易于调试**:单线程程序的调试相对容易,因为不存在多个线程之间复杂的交互情况。 3. **资源消耗少**:单线程程序创建和切换线程的开销较小,对于资源有限的环境更为友好。 然而,单线程也有其局限性: 1. **...
### Java线程入门知识点详解 #### 一、Java线程基础知识概述 **1.1 什么是线程?** 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在Java中...
同时,也要注意多线程编程中的死锁、活锁和饥饿等问题,合理设计线程间的交互,避免出现不可预期的行为。在实际开发中,合理运用线程同步和通信机制,能够提高系统的响应速度和并发处理能力,提升软件的用户体验。
在Java编程中,多线程是一项关键特性,它允许程序同时执行多个任务,提升系统效率。在处理耗时操作如大文件下载、数据处理或网络请求时,展示进度条能够提供用户友好的交互体验,让使用者了解任务的完成状态。本主题...
下面我们将详细讨论如何使用Java线程来实现这一功能。 首先,我们需要理解线程的基本概念。线程是程序执行的最小单元,一个进程可以包含多个线程,每个线程都有自己的程序计数器、寄存器和栈空间,但共享同一块内存...
Java线程安全是多线程编程中的一个关键概念,它涉及到多个线程访问共享资源时可能出现的问题。在Java中,线程安全问题通常与并发、内存模型和可见性有关。Java内存模型(JMM)定义了如何在多线程环境下共享数据的...