网站首页 > 精选教程 正文
概述
在本文中,我们将讨论 Java 线程状态,特别是Thread.State.WAITING。 我们将研究线程进入此状态的方法以及它们之间的区别。 最后,我们将仔细研究 LockSupport 类,它提供了几个用于同步的实用的静态方法。
进入Thread.State.WAITING
Java 提供了多种将线程置于 WAITING 状态的方法。
- Object.wait()
我们可以将线程置于 WAITING 状态的最标准方法之一是通过 wait() 方法。 当一个线程拥有一个对象的监听器时,我们可以暂停它的执行,直到另一个线程完成工作并使用 notify() 方法将其唤醒。
package com.toutiao.treadwaiting;
public class TreadWaitingApplication {
private static Object lock = new Object();
public static void main(String[] args) {
//Thread1
new Thread(() -> {
System.out.println(Thread.currentThread());
synchronized (lock) {
System.out.println("线程1开始执行");
try {
lock.wait();
System.out.println("线程1执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "thread1").start();
}
}
执行main方法,thread1线程处于等待状态
当执行暂停时,线程处于 WAITING(在对象监听器上)状态,这也会在程序的线程堆栈中打印出来。
切换到终端命令行,使用java自带工具执行:jstack pid,可以都看到thread1的堆栈信息,处于waiting:
- Tread.join()
我们可以用来暂停线程执行的另一种方法是通过 join() 调用。 当我们的主线程需要等待工作线程首先完成时,我们可以从主线程调用工作线程实例上的 join() 方法将主线程暂停,主线程将进入 WAITING 状态
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().setName("【主线程】");
System.out.println("主线程开始执行");
//Thread1
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"拼命工作中");
try {
Thread.sleep(5000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "【子线程1】");
thread1.start();
System.out.println("从主线程中执行线程1的join方法");
thread1.join();
System.out.println("主线程执行完毕");
}
从 jstack 报告为 WAITING(在对象监视器上):
子线程则处于睡眠状态(因为调用了sleep方法来模拟耗时执行)
LockSupport.park()
最后,我们还可以使用 LockSupport 类的 park() 静态方法将线程设置为 WAITING 状态。 调用 park() 将停止当前线程的执行并将其置于 WAITING 状态——更具体地说,jstack 报告将显示 WAITING (parking) 状态:
@Test
public void testLockSupport(){
System.out.println(Thread.currentThread().getName()+"开始执行");
System.out.println(Thread.currentThread().getName()+"执行LockSupport.park方法");
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
查看堆栈信息,mian线程处于waiting:
等待和取消等待线程
正如我们之前看到的,我们可以使用 LockSupport 类提供的工具来等待和取消等待线程。 这个类是 Unsafe 类的一个包装器,它的大部分方法都会立即委托给它。 但是,由于 Unsafe 被认为是内部 Java API 并且不应该使用,LockSupport 是官方方式。
如何使用锁支持
使用 LockSupport 很简单。 如果我们想停止线程的执行,我们调用 park() 方法。 我们不必提供对线程对象本身的引用——代码会停止调用它的线程。
让我们看一个简单的等待示例:
@Test
public void testLockSupport2() throws InterruptedException {
Thread t = new Thread(() -> {
int acc = 0;
for (int i = 1; i <= 100; i++) {
acc += i;
}
System.out.println("工作完成");
LockSupport.park();
System.out.println(acc);
});
t.setName("PARK线程");
t.start();
}
我们创建了一个最小的控制台应用程序,它累积从 1 到 100 的数字并将它们打印出来。 如果我们运行它,我们会看到它打印的是 工作完成 但不是结果。 当然,这是因为我们之前调用了 park() 。
要让 PARK-THREAD 完成,我们必须将其取消等待。 为此,我们必须使用不同的线程。 我们可以使用主线程(运行 main() 方法的线程)或创建一个新线程。
为简单起见,让我们使用主线程:
t.setName("PARK-THREAD");
t.start();
Thread.sleep(1000);
LockSupport.unpark(t);
我们在主线程中添加一秒休眠,让 PARK-THREAD 完成积累并自行停放。之后,我们通过调用 unpark(Thread) 来取消它。正如预期的那样,在 unparking 期间,我们必须提供对要启动的线程对象的引用。
通过我们的更改,程序现在可以正确终止并打印结果 5050。
PARK与WAIT
由于这两个 API 都为我们提供了相似的功能,我们应该更喜欢哪一个?通常,LockSupport 类及其工具被认为是低级 API。此外,API 很容易被误用,导致难以理解的死锁。大多数情况下,我们应该使用 Thread 类的 wait() 和 join()。
使用park的好处是我们不需要进入同步块来禁用线程。这很重要,因为同步块在代码中需要同步代码块,这会强制刷新所有变量,如果不需要,可能会降低性能。然而,这种性能损耗几乎可以忽略
结论
在本文中,我们探索了 LockSupport 类及其park API。我们研究了如何使用它来禁用线程,并在内部解释了它是如何工作的。最后,我们将它与更常见的 wait()/join() API 进行了比较,并展示了它们的区别。
猜你喜欢
- 2024-11-18 Java并发编程线程状态转换
- 2024-11-18 Java面试必考问题:线程的生命周期
- 2024-11-18 Java 19 虚拟线程的状态变化,停驻与锁定
- 2024-11-18 Java线程生命周期详解?
- 2024-11-18 线程从创建最终消亡,要经历的若干状态,你了解其中的多少?
- 2024-11-18 浅谈Java线程:线程基础知识扫盲
- 2024-11-18 Java线程在各个状态下调用start方法会发生什么事情?
- 2024-11-18 为什么Java中线程没有Running状态
- 2024-11-18 阻塞模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?
- 2024-11-18 可动态调节参数的线程池实现
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)