回答
要想保证 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)
,一样可以得到。