0%

如何控制多线程的执行顺序?

如何去控制多线程的执行顺序?

join方式

我们直接通过在每个Thread对象后面使用join方法就可以实现线程的顺序执行,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}

public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 4; i++) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.join();
}
System.out.println("我是主线程");
}
}

多次执行结果都为下面这种情况:

用join方法来保证线程顺序,其实就是让main这个主线程等待子线程结束,然后主线程再执行接下来的其他线程任务,点进去join方法我们可以了解的更透彻:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}

源码中的参数millis默认值是0,从英文注释翻译后可以找到,0秒意味着永远等待,也就是threadDemo执行不完,那主线程你就要一直等着,一直wait,而代码中wait方法其实就是属于Object的方法,负责线程的休眠等待,当主线程调用threadDemo.join的时候,主线程会获得线程对象threadDemo的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒主线程。这就意味着主线程调用threadDemo.join时,必须能够拿到threadDemo线程对象的锁。

ExecutorService方式

首先看一下代码,我们如何通过这种方式实现线程顺序执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo2 {
static ExecutorService executorService = Executors.newSingleThreadExecutor();

public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
int temp = i;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread" + temp);
}
});
executorService.submit(thread);
}
executorService.shutdown();
}
}

最终的多次执行结果均为有序的,如下图:

解释一下,这种方式的原理其实就是将线程用排队的方式扔进一个线程池里,让所有的任务以单线程的模式,按照FIFO先进先出、LIFO后进先出、优先级等特定顺序执行,但是这种方式也是存在缺点的,就是当一个线程被阻塞时,其它的线程都会受到影响被阻塞,不过依然都会按照自身调度来执行,只是会存在阻塞延迟。