Java 中 线程A B C 三个线程如何让其执行顺序为C-B-A
由于存在CPU调度的不确定性,所以多线程的执行顺序具有不确定性。主线程有可能比其他线程先执行完,其他线程也有可能比主线程执行完,其他线程之间执行顺序也可能不同 ,那么想让线程顺序执行,那就要用特定的方法来搞定!
· [1] 使用线程的join方法
· [2] 使用主线程的join方法
· [3] 使用线程的wait方法
· [4] 使用线程的线程池方法
· [5] 使用线程的Condition(条件变量)方法
· [6] 使用线程的CountDownLatch(倒计数)方法
· [7] 使用线程的CyclicBarrier(回环栅栏)方法
· [8] 使用线程的Semaphore(信号量)方法
就随便拿CountDownLatch 和 CyclicBarrier玩一玩!!!
1 使用线程的CountDownLatch(倒计数)方法
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
@Slf4j
public class TestCountDownLatch {
private static CountDownLatch latch = new CountDownLatch(1);
private static CountDownLatch latch2 = new CountDownLatch(1);
public static void main(String[] args) {
Runnable r=()->{
try {
//等待B线程计数为0时,才往下执行
latch2.await();
log.info("running");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(r, "A");
Runnable r2=()->{
try {
//等待C线程计数为0时,才往下执行
latch.await();
log.info("r2 running");
//B线程开始计数
latch2.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread t2 = new Thread(r2, "B");
Runnable r3=()->{
log.info("r3 running");
//C线程开始计数
latch.countDown();
};
Thread t3 = new Thread(r3, "C");
t3.start();
t1.start();
t2.start();
}
}
2 使用线程的CyclicBarrier(回环栅栏)方法
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
@Slf4j
public class TestCyclicBarrier {
static CyclicBarrier barrier1 = new CyclicBarrier(2);
static CyclicBarrier barrier2 = new CyclicBarrier(2);
public static void main(String[] args) {
Runnable r=()->{
try {
//放开栅栏2
barrier2.await();
log.info("running");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(r, "A");
Runnable r2=()->{
try {
//放开栅栏1
barrier1.await();
log.info("r2 running");
//放开栅栏2
barrier2.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
Thread t2 = new Thread(r2, "B");
Runnable r3=()->{
try {
log.info("r3 running");
//放开栅栏1
barrier1.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
Thread t3 = new Thread(r3, "C");
t3.start();
t1.start();
t2.start();
}
}
执行结果:
00:13:14.520 [C] INFO cn.hb.test.TestCyclicBarrier - r3 running
00:13:14.520 [B] INFO cn.hb.test.TestCyclicBarrier - r2 running
00:13:14.520 [A] INFO cn.hb.test.TestCyclicBarrier - running
3. CountDownLatch 和 CyclicBarrier的区别?
1、CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。
2、cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!
3、CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
4,、CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false
CountDownLatch的使用场景:
在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作。 这个时候就可以使用CountDownLatch。CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。
CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。