最近总是遇到这样的问题:怎么控制线程访问顺序?怎么让线程B在线程A执行之后再执行?怎么让线程A、B、C按顺序打印ABCABC? 以上都可以归结为线程的顺序执行问题,有这么4种方案:
(1)join():“等你执行结束,我再执行”
(2)singleThreadPool:只有一个线程的线程池,任务乖乖在队列中等待被执行
(3)wait/notify机制:“兄弟们,醒醒,到你了”
(4)ReentrantLock 的 condition 的 await/signal机制:“那个兄弟,醒醒,到你了”
接下来就这四种方式进行详细说明
1、简介:join(),等待线程结束,才能继执行。
2、本质:让线程wait在当前线程对象实例上,观察join的源码:
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;
}
}
}
解读:阻塞当前线程,直到目标线程运行结束,如果参数是0,意味着无限等待。
3、join实现线程顺序执行
(1)方式1:在run()中调用join方法。
public class M1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> System.out.println("A"));
Thread t2 = new Thread(() -> {
try {
t1.join(); //瞧,线程2等待线程1执行结束,然后再执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");});
Thread t3 = new Thread(() -> {
try {
t2.join(); //线程3等待线程2执行结束,然后再执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");});
t1.start();
t3.start();
t2.start();
//输出:A B C
}
}
(2)方式2:在主方法中调用join
public class M2 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> System.out.println("A"));
Thread t2 = new Thread(() -> System.out.println("B"));
Thread t3 = new Thread(() -> System.out.println("C"));
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
//输出:A B C
}
}
1、简介:只有一个线程的线程池,任务都在等待队列中排队,所以它们是一个一个执行的
2、两种创建方式:
ExecutorService single=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
ExecutorService single = Executors.newSingleThreadExecutor();
3、单线程线程池实现顺序执行
public class M2 {
public static void main(String[] args){
Thread t5=new Thread(()->System.out.println("A"));
Thread t6=new Thread(()->System.out.println("B"));
Thread t7=new Thread(()->System.out.println("C"));
ExecutorService single=new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
single.submit(t5);
single.submit(t7);
single.submit(t6);
}
}
1、简介:有多种实现方式,这里介绍通过一个变量的取值来控制线程执行顺序。
注意:wait/notify 要求获得对象的monitor,所以只能在同步方法或同步代码块中使用
2、wait/notify实现线程顺序执行
class MyThread {
private static int flag=0;
//str:稍后要打印的值;order:取值范围[0,1,2],分别代表先后顺序
public synchronized void printStr(String str,int order){
while (flag!=order){
try{
this.wait(); //否则释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(str);
if (order<2)
flag++;
else
flag=1;
this.notifyAll(); //唤起其他线程
}
}
public class M3 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread t8 = new Thread(new Runnable() {
@Override
public void run() {
myThread.printStr("A", 0); //第一个,打印A
}
});
Thread t9 = new Thread(new Runnable() {
@Override
public void run() {
myThread.printStr("B", 2); //第3,打印B
}
});
Thread t10 = new Thread(new Runnable() {
@Override
public void run() {
myThread.printStr("C", 1); //第二,打印C
}
});
t8.start();
t9.start();
t10.start();
//执行结果:A C B
}
}
1、简介:这个机制和wait/notify很像,(笔者狭隘地认为,condition一定情况下可以更加精确~)
2、Conditon:await/signal要配合ReentrantLock使用,通过ReentrantLock的newConditon方法生成一个与当前重入锁绑定的Condition实例,让我们能在线程何时的时间等待,或在一个特定的时刻获得通知。阻塞队列之所以能够阻塞,底层也是利用了condition哦
3、await/signal实现线程顺序执行
public class M5{
private static Lock lock=new ReentrantLock();
private static Condition condition1=lock.newCondition();
private static Condition condition2=lock.newCondition();
private static boolean printA=true; //先打印A
private static boolean printB=false; //先打印B
public static void main(String[] args) throws InterruptedException {
Thread t11=new Thread(() -> {
try{
lock.lock();
if (!printA) {
condition1.await(); //如果当前printA=false,则在条件1上等待
}
System.out.println("A");
printB=true;
printA=false;
condition2.signal(); //唤醒在condition2上等待的线程
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
});
Thread t12=new Thread(() -> {
try{
lock.lock();
if (!printB){
condition2.await();
}
System.out.println("B");
printA=true;
printB=false;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
});
t11.start();
t12.start();
}
}
参考资料:
1、《Java高并发程序设计》
2、让线程按顺序执行8种方法:https://www.cnblogs.com/wenjunwei/p/10573289.html