回答
要想保证 T1
、T2
、T3
顺序执行,有多种方法。我们先新建一个 Tread:
class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
this.threadName = name;
}
@Override
public void run() {
System.out.println(threadName + " is running");
}
}
一、使用 join()
join()
是 Java 提供的一种用于线程同步的机制,它能够让一个线程等待另一个线程完成后再继续执行。它有三个重载方法:
join()
:无限等待,直到目标线程执行完毕。join(long millis)
:等待目标线程执行完毕,或者达到指定的毫秒时间。join(long millis, int nanos)
:等待目标线程执行完毕,或者达到指定的时间(以毫秒和纳秒为单位)。
使用 join()
来实现 T1
、T2
、T3
顺序执行,非常简单:
public class JoinTest {
public static void main(String[] args) {
MyThread T1 = new MyThread("T1");
MyThread T2 = new MyThread("T2");
MyThread T3 = new MyThread("T3");
try {
T1.start();
T1.join(); // 等待 T1 执行完毕
T2.start();
T2.join(); // 等待 T2 执行完毕
T3.start();
T3.join(); // 等待 T3 执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 执行结果
T1 is running
T2 is running
T3 is running
方法二:CountDownLatch
CountDownLatch
是一个同步辅助类,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
CountDownLatch
内部是通过一个计数器来实现的,当我们在构造一个CountDownLatch对象的时候需要传 入该计数器值,该值就表示了线程的数量。当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。
使用 CountDownLatch
来实现 T1
、T2
、T3
顺序执行,如下 :
class MyThread extends Thread {
private String threadName;
private CountDownLatch currentLatch;
private CountDownLatch nextLatch;
public MyThread(String name, CountDownLatch currentLatch, CountDownLatch nextLatch) {
this.threadName = name;
this.currentLatch = currentLatch;
this.nextLatch = nextLatch;
}
@Override
public void run() {
try {
currentLatch.await(); // 等待当前的 CountDownLatch 减到 0
System.out.println(threadName + " is running.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
nextLatch.countDown(); // 减少下一个 CountDownLatch 的计数
}
}
}
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch latch1 = new CountDownLatch(1);
CountDownLatch latch2 = new CountDownLatch(1);
CountDownLatch latch3 = new CountDownLatch(1);
MyThread T1 = new MyThread("T1", latch1, latch2);
MyThread T2 = new MyThread("T2", latch2, latch3);
MyThread T3 = new MyThread("T3", latch3, new CountDownLatch(0));
T1.start();
T2.start();
T3.start();
latch1.countDown(); // 启动 T1,计数 -1 变为 0
}
}
在这里,我们创建了三个 CountDownLatch
,分别用于控制 T1
、T2
、T3
的启动顺序。每个线程在启动前都等待当前的 CountDownLatch
变为 0,然后执行任务,并减少下一个 CountDownLatch
的计数。
当三个线程启动时,都会在那里等待,T1
等待latch1
变成 0
,T2
等待latch2
变成 0
,T3
等待latch3
变成 0
。当在 main()
中执行 latch1.countDown()
时,T1
则启动执行,执行完成后执行 latch2.countDown()
驱动 T3 执行。
方案三 :Semaphore
Semaphore
是一个计数信号量,最常用于限制某个资源可被同时访问的线程数。
Semaphore
是一个非负整数(>=1
)。当一个线程想要访问某个共享资源时,它必须要先获取Semaphore
,当Semaphore > 0
时,获取该资源并使Semaphore – 1
。如果Semaphore = 0
,则表示全部的共享资源已经被其他线程全部占用,线程必须要等待其他线程释放资源。当线程释放资源时,Semaphore +1
。
使用 Semaphore
来实现 T1
、T2
、T3
顺序执行,如下 :
class MyThread extends Thread {
private String threadName;
private Semaphore currentSemaphore;
private Semaphore nextSemaphore;
public MyThread(String name, Semaphore currentSemaphore, Semaphore nextSemaphore) {
this.threadName = name;
this.currentSemaphore = currentSemaphore;
this.nextSemaphore = nextSemaphore;
}
@Override
public void run() {
try {
currentSemaphore.acquire(); // 获取当前的 Semaphore 许可
System.out.println(threadName + " is running.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
nextSemaphore.release(); // 释放下一个 Semaphore 的许可
}
}
}
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore1 = new Semaphore(1); // 初始信号量许可为 1
Semaphore semaphore2 = new Semaphore(0); // 初始信号量许可为 0
Semaphore semaphore3 = new Semaphore(0); // 初始信号量许可为 0
MyThread T1 = new MyThread("T1", semaphore1, semaphore2);
MyThread T2 = new MyThread("T2", semaphore2, semaphore3);
MyThread T3 = new MyThread("T3", semaphore3, new Semaphore(0));
T1.start();
T2.start();
T3.start();
}
}
创建三个 Semaphore
实例,第一个的许可数为 1,其他两个为 0。每个线程在启动前都获取当前的 Semaphore
许可,执行任务后释放下一个 Semaphore
的许可。
最后,其实实现 T1
、T2
、T3
的方式有很多种,比如最笨的方式直接使用 wait()
就可以了,T1 wait(10)
、T2 wait(20)、T3 wait(30)
,一样可以得到。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。