网站首页 > 文章精选 正文
wait()方法
- 定义和所属类:wait()方法是java.lang.Object类中的一个方法。这意味着在 Java 中,任何对象都可以调用这个方法。
- 作用机制:
- 当一个线程调用一个对象的wait()方法时,它会释放该对象的锁,然后进入等待状态。这个线程会一直等待,直到其他线程调用该对象的notify()或notifyAll()方法来唤醒它。例如,在一个生产者 - 消费者模型中,消费者线程在发现缓冲区为空时,可以调用wait()方法等待生产者生产数据。当生产者生产了数据并调用notify()方法后,消费者线程就会被唤醒,重新获取对象的锁并继续执行。
- 线程在wait()状态下是处于阻塞状态的一种,它会被放入对象的等待队列中,这个队列是和对象关联的,用于管理等待这个对象的线程。
- 使用场景:主要用于线程之间的协作和同步。比如在多线程访问共享资源(如共享数据结构)时,通过wait()和notify()可以实现对资源访问的有效控制,避免数据竞争和不一致性。
- 注意事项:调用wait()方法必须在同步代码块或者同步方法中,因为wait()方法会释放对象的锁,所以需要先获取锁才能释放。否则会抛出IllegalMonitorStateException异常。
- 示例代码:
import java.util.ArrayList;
import java.util.List;
class ProducerConsumerExample {
private List<Integer> buffer = new ArrayList<>();
private final int bufferSize = 5;
public synchronized void produce() {
while (buffer.size() == bufferSize) {
try {
// 如果缓冲区已满,生产者线程等待
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
buffer.add(1);
System.out.println("Produced. Buffer size: " + buffer.size());
// 通知消费者线程可以消费了
notify();
}
public synchronized void consume() {
while (buffer.isEmpty()) {
try {
// 如果缓冲区为空,消费者线程等待
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
buffer.remove(0);
System.out.println("Consumed. Buffer size: " + buffer.size());
// 通知生产者线程可以生产了
notify();
}
}
public class WaitExample {
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
Thread producerThread = new Thread(example::produce);
Thread consumerThread = new Thread(example::consume);
producerThread.start();
consumerThread.start();
}
}
在上述代码中,生产者线程在缓冲区已满时调用wait()方法等待,消费者线程在缓冲区为空时也调用wait()方法等待。当生产者生产了一个数据后,通过notify()方法通知消费者线程,当消费者消费了一个数据后,通过notify()方法通知生产者线程。
sleep()方法
- 定义和所属类:sleep()方法是java.lang.Thread类中的一个静态方法。这意味着它是通过线程类来调用的,而不是像wait()那样通过对象来调用。
- 作用机制:
- 当一个线程调用Thread.sleep(long millis)方法时,它会使当前线程暂停执行指定的毫秒数。在这个期间,线程进入阻塞状态,但和wait()不同的是,它不会释放对象的锁(如果线程持有锁的话)。例如,如果一个线程在同步方法中调用sleep()方法,它依然会持有该方法所属对象的锁,其他线程无法访问这个同步方法,直到sleep()时间结束。
- 线程在sleep()结束后,会重新进入就绪状态,等待 CPU 调度,然后继续执行。
- 使用场景:用于让线程暂停一段时间,比如模拟延迟、定时任务等。例如,一个每隔一段时间就检查系统状态的线程,可以通过sleep()方法来实现定时检查。
- 注意事项:sleep()方法可能会抛出InterruptedException异常。这个异常通常在其他线程调用当前线程的interrupt()方法时抛出。所以在调用sleep()方法时,通常需要在try - catch块中处理这个异常。
- 示例代码:
public class SleepExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("Thread started.");
// 线程暂停 3 秒
Thread.sleep(3000);
System.out.println("Thread resumed after 3 seconds.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
}
}
在这个例子中,创建了一个线程,该线程在启动后先打印一条消息,然后调用sleep(3000)让线程暂停 3 秒,3 秒后继续执行并打印另一条消息。
yield()方法
- 定义和所属类:yield()方法是java.lang.Thread类中的一个静态方法。
- 作用机制:
- 当一个线程调用yield()方法时,它是在暗示调度器自己当前可以让出 CPU 资源。但是调度器是否会真正让当前线程暂停执行,这是不确定的。它可能会让当前线程暂停,然后重新进入就绪队列,等待下一次调度;也可能会忽略这个请求,让线程继续执行。
- 线程调用yield()方法后,会从运行状态转换到就绪状态,它不会像sleep()那样进入阻塞状态,也不会释放对象的锁(如果线程持有锁的话)。
- 使用场景:用于在多线程环境中,当一个线程完成了一部分任务,并且认为其他线程可能更需要 CPU 资源时,可以调用yield()方法来给其他线程机会执行。不过由于调度器的不确定性,它在实际应用中的效果可能不太好预测。
- 注意事项:yield()方法是一种启发式的方法,不能保证一定能达到预期的调度效果。在不同的操作系统和 JVM 实现中,yield()方法的行为可能会有所不同。
- 示例代码:
public class YieldExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1: " + i);
// 偶尔让出 CPU 资源
if (i % 2 == 0) {
Thread.yield();
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2: " + i);
// 偶尔让出 CPU 资源
if (i % 3 == 0) {
Thread.yield();
}
}
});
thread1.start();
thread2.start();
}
}
在这个例子中,创建了两个线程,它们在循环中偶尔调用yield()方法尝试让出 CPU 资源,但实际效果取决于调度器的决策。
join()方法
- 定义和所属类:join()方法是java.lang.Thread类中的一个方法。
- 作用机制:
- 当在一个线程中调用另一个线程的join()方法时,当前线程会被阻塞,直到被调用join()方法的线程执行完毕。例如,有线程 A 和线程 B,在 A 中调用 B.join (),那么 A 会暂停执行,等待 B 完成后 A 才会继续执行。
- 使用场景:用于协调线程的执行顺序。比如在主线程中,如果需要等待某个子线程完成特定任务后再继续执行后续操作,就可以使用join()方法。
- 注意事项:join()方法可能会抛出InterruptedException异常。这个异常通常在当前线程被中断时抛出。所以在调用join()方法时,通常也需要在try - catch块中处理这个异常。
- 示例代码:
public class JoinExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("Sub - thread started.");
// 模拟子线程执行一些耗时任务
Thread.sleep(2000);
System.out.println("Sub - thread finished.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
try {
System.out.println("Main thread is waiting for sub - thread to finish.");
// 主线程等待子线程完成
thread.join();
System.out.println("Main thread continues after sub - thread finished.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
在这个例子中,主线程启动一个子线程后,调用join()方法等待子线程完成。子线程在执行过程中会暂停 2 秒来模拟耗时任务,当子线程完成后,主线程才会继续执行。
异同点总结
- 相同点:
- 状态转换:wait()、sleep()、yield()和join()方法都会导致线程状态的改变。wait()和sleep()方法会使线程进入阻塞状态(wait()是无限等待或等待被唤醒,sleep()是限时等待),yield()方法会使线程从运行状态转换到就绪状态,join()方法会使当前线程进入阻塞状态,直到被调用join()方法的线程执行完毕。
- 影响执行顺序:它们都在一定程度上影响了线程的执行顺序。wait()是等待其他线程唤醒,sleep()是暂停一段时间后再继续执行,yield()是让出 CPU 资源(可能),join()是等待另一个线程完成,这些操作都会改变线程原本的执行顺序。
- 不同点:
- 所属类和调用方式:wait()是Object类的方法,需要通过对象来调用;sleep()和yield()、join()是Thread类的方法,sleep()和join()是实例方法,yield()是静态方法,sleep()是通过Thread.sleep()调用,yield()是通过Thread.yield()调用,join()是通过线程对象的join()方法调用。
- 锁的释放:wait()方法会释放对象的锁,而sleep()、yield()和join()方法不会释放对象的锁(如果线程持有锁的话)。
- 等待性质:wait()是等待其他线程通过notify()或notifyAll()来唤醒,是一种基于对象通知的等待;sleep()是基于时间的等待,线程会在指定时间后自动恢复;yield()是一种协作式的让出 CPU 资源,调度器是否接受这个请求是不确定的;join()是等待指定线程执行完毕的等待。
猜你喜欢
- 2024-12-26 Java高级:条件队列与同步器Synchronizer的原理+AQS的应用
- 2024-12-26 浅谈Java多线程与并发原理 java多线程并发调用接口
- 2024-12-26 Java 基础(四)集合源码解析 List java集合linkedlist
- 2024-12-26 synchronized和lock的区别 54.synchronized 和 lock 有什么区别?
- 2024-12-26 异步 vs 同步:程序员必备的核心知识,理解这两者差异,你就是高手
- 2024-12-26 ArrayList 、 LinkedList、Vector的区别
- 2024-12-26 java面试基础题(实战后的总结) java面试必考300题
- 2024-12-26 synchronized底层细究(硬核) synchronized底层原理是什么
- 2024-12-26 为什么 95% 的 Java 程序员,都是用不好 Synchronized?
- 2024-12-26 100+道高频Java面试题 java面试高频知识点
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)