정상 작동입니다. 세 개의 스레드를 시작하고 실행하도록 하세요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class ThreadDemo {
public static void main(String[] args) {
final Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程1" );
}
});
final Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程2" );
}
});
Thread t3 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程3" );
}
});
t1.start();
t2.start();
t3.start();
}
}
|
로그인 후 복사
실행 결과: 세 스레드의 시작 메서드를 호출합니다. 분명히 순서대로 호출되지만 각 실행의 결과는 기본적으로 다르며 무작위성이 특히 강합니다. 무엇을 해야 할까요? 아래에서는 이를 달성하기 위해 네 가지 솔루션을 사용합니다. 옵션 1
Thread의 Join 메서드를 사용하여 스레드 순서 문제를 해결할 수 있습니다.
공식 소개: 이 스레드가 죽기를 기다립니다.
等待这个线程结束,也就是说当前线程等待这个线程结束后再继续执行 。
join()
方法是Thread
中的一个public
方法,它有几个重载版本:
join(long millis)
//参数为毫秒join(long millis,int nanoseconds)
//第一参数为毫秒,第二个参数为纳秒
join()方法实际是利用了wait()
方法(wait方法是Object中的),只不过它不用等待notify()/notifyAll()
,且不受其影响。
它结束的条件是:
- 目标线程已经run完(通过
isAlive()
方法来判断)
下面大致看看器源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public final void join() throws InterruptedException {
join(0);
}
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;
}
}
}
|
로그인 후 복사
下面我们使用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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class ThreadDemo {
public static void main(String[] args) {
final Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程1" );
}
});
final Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "线程2" );
}
});
Thread t3 = new Thread( new Runnable() {
@Override
public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "线程3" );
}
});
t3.start();
t2.start();
t1.start();
}
}
|
로그인 후 복사
运行结果:
不管你运行多少次上面这段代码,结果始终不变,所以,我们就解决了多个线程按照顺序执行的问题了。
下面我们来看看另外一种方案:CountDownLatch
。
方案二
我们先来说一下CountDownLatch
,然后再来使用CountDownLatch
是怎么解决多个线程顺序执行的。
CountDownLatch
是一种同步辅助,在AQS基础之上实现的一个并发工具类,让我们多个线程执行任务时,需要等待线程执行完成后,才能执行下面的语句,之前线程操作时是使用 Thread.join
方法进行等待 。
CountDownLatch
能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。它相当于是一个计数器,这个计数器的初始值就是线程的数量,每当一个任务完成后,计数器的值就会减一,当计数器的值为 0 时,表示所有的线程都已经任务了,然后在 CountDownLatch
上等待的线程就可以恢复执行接下来的任务。
下面我们就用CountDownLatch
来实现多个线程顺序执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | import java.util.concurrent.CountDownLatch;
public class ThreadDemo {
public static void main(String[] args) {
CountDownLatch countDownLatch1 = new CountDownLatch(0);
CountDownLatch countDownLatch2 = new CountDownLatch(1);
CountDownLatch countDownLatch3 = new CountDownLatch(1);
Thread t1 = new Thread( new Work(countDownLatch1, countDownLatch2), "线程1" );
Thread t2 = new Thread( new Work(countDownLatch2, countDownLatch3), "线程2" );
Thread t3 = new Thread( new Work(countDownLatch3, countDownLatch3), "线程3" );
t1.start();
t2.start();
t3.start();
}
static class Work implements Runnable {
CountDownLatch cOne;
CountDownLatch cTwo;
public Work(CountDownLatch cOne, CountDownLatch cTwo) {
super();
this.cOne = cOne;
this.cTwo = cTwo;
}
@Override
public void run() {
try {
cOne.await();
System.out.println( "执行: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
cTwo.countDown();
}
}
}
}
|
로그인 후 복사
运行结果:
关于CountDownLatch
实现多个线程顺序执行就这样实现了,下面我们再用线程池来实现。
方案三
在Executors 类中有个单线程池的创建方式,下面我们就用单线程池的方式来实现多个线程顺序执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo {
public static void main(String[] args) {
Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程1" );
}
}, "线程1" );
Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程2" );
}
}, "线程2" );
Thread t3 = new Thread( new Runnable() {
@Override
public void run() {
System.out.println( "线程3" );
}
});
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(t1);
executor.submit(t2);
executor.submit(t3);
executor.shutdown();
}
}
|
로그인 후 복사
运行结果:
这样我们利用单线程池
也实现了多个线程顺序执行的问题。下面再来说一种更牛的方案。
方案四
最后一种方案是使用CompletableFuture
来实现多个线程顺序执行。
在Java 8问世前想要实现任务的回调,一般有以下两种方式:
- 借助
Future isDone
轮询以判断任务是否执行结束,并获取结果。 Future isDone
轮询以判断任务是否执行结束,并获取结果。
借助Guava
类库ListenableFuture
、FutureCallback
。(netty也有类似的实现)Java 8 CompletableFuture
弥补了Java在异步编程方面的弱势。
在Java中异步编程,不一定非要使用rxJava,Java本身的库中的CompletableFuture
可以很好的应对大部分的场景。
Java8
新增的CompletableFuture
借助
구아바
类库
ListenableFuture
、
FutureCallback
。(netty也有类似的实现)
Java 8 CompletableFuture
弥补了Java는 异步编程方face的弱势。🎜
Java中异步编程, 不一定不要使用rxJava,Java本身的库中的CompletableFuture
可以很好的应对大辑景。🎜
Java8
새로운 CompletableFuture
则借鉴了Netty等对Future的改造,简化了异步编程的复杂性,并且提供了函数式编程的能力 . 🎜
使用Future
获得异步执行结果时,要么调用阻塞方法get()
,要么轮询看isDone()
是否为true
,这两种方法都不是很好,因为主线程也会被迫等待。
从Java 8开始引入了CompletableFuture
,它针对Future
做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
接下来我们就使用CompletableFuture
来实现多个线程顺序执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import java.util.concurrent.CompletableFuture;
public class ThreadDemo {
public static void main(String[] args) {
Thread t1 = new Thread( new Work(), "线程1" );
Thread t2 = new Thread( new Work(), "线程2" );
Thread t3 = new Thread( new Work(), "线程3" );
CompletableFuture.runAsync(()-> t1.start())
.thenRun(()->t2.start())
.thenRun(()->t3.start());
}
static class Work implements Runnable{
@Override
public void run() {
System.out.println( "执行 : " + Thread.currentThread().getName());
}
}
}
|
로그인 후 복사
运行结果:
到此,我们就使用CompletableFuture
实现了多个线程顺序执行的问题。
总结
关于多个线程顺序执行,不管是对于面试,还是工作,关于多线程顺序执行的解决方案都是非常有必要掌握的。也希望下次面试官再问:多线程顺序执行问题的时候,你的表情应该是这样的:

위 내용은 멀티 스레드 순차 실행, 두 가지 유형만 알고 계십니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!