如果你使用了多线程,就必须接受一个非常现实的问题,即顺序问题。
假如之前代码的执行顺序是:a,b,c,改成多线程执行之后,代码的执行顺序可能变成了:a,c,b。(这个跟cpu调度算法有关)。
例如:
public static void main(String[] args) {
Thread thread1 = new Thread(() -> System.out.println("a"));
Thread thread2 = new Thread(() -> System.out.println("b"));
Thread thread3 = new Thread(() -> System.out.println("c"));
thread1.start();
thread2.start();
thread3.start();
}执行结果:
a
c
b那么,来自灵魂的一问:如何保证线程的顺序呢?
即线程启动的顺序是:a,b,c,执行的顺序也是:a,b,c。
如下图所示:
3.1 join
Thread类的join方法它会让主线程等待子线程运行结束后,才能继续运行。
列如:
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> System.out.println("a"));
Thread thread2 = new Thread(() -> System.out.println("b"));
Thread thread3 = new Thread(() -> System.out.println("c"));
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
}执行结果永远都是:
a
b
c3.2 newSingleThreadExecutor
我们可以使用JDK自带的Excutors类的newSingleThreadExecutor方法,创建一个单线程的线程池。
例如:
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Thread thread1 = new Thread(() -> System.out.println("a"));
Thread thread2 = new Thread(() -> System.out.println("b"));
Thread thread3 = new Thread(() -> System.out.println("c"));
executorService.submit(thread1);
executorService.submit(thread2);
executorService.submit(thread3);
executorService.shutdown();
}执行结果永远都是:
a
b
c使用Excutors类的newSingleThreadExecutor方法创建的单线程的线程池,使用了LinkedBlockingQueue作为队列,而此队列按 FIFO(先进先出)排序元素。
添加到队列的顺序是a,b,c,则执行的顺序也是a,b,c。
3.3 CountDownLatch
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。
例如:
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch1 = new CountDownLatch(0);
CountDownLatch latch2 = new CountDownLatch(1);
CountDownLatch latch3 = new CountDownLatch(1);
Thread thread1 = new Thread(new TestRunnable(latch1, latch2, "a"));
Thread thread2 = new Thread(new TestRunnable(latch2, latch3, "b"));
Thread thread3 = new Thread(new TestRunnable(latch3, latch3, "c"));
thread1.start();
thread2.start();
thread3.start();
}
}
class TestRunnable implements Runnable {
private CountDownLatch latch1;
private CountDownLatch latch2;
private String message;
TestRunnable(CountDownLatch latch1, CountDownLatch latch2, String message) {
this.latch1 = latch1;
this.latch2 = latch2;
this.message = message;
}
@Override
public void run() {
try {
latch1.await();
System.out.println(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch2.countDown();
}
}执行结果永远都是:
a
b
c此外,使用CompletableFuture的thenRun方法,也能多线程的执行顺序,在这里就不一一介绍了。