Java:现有线程T1/T2/T3,如何确保T1执行完成之后执行T2,T3在T2执行完成之后执行。

时间:2021-05-23 00:46:31

要实现多个线程执行完成先后,就要知道如何实现线程之间的等待,java线程等待实现是join。java的jdk中join方法实现如下:

 1 public final synchronized void join(long millis)
 2     throws InterruptedException {
 3         long base = System.currentTimeMillis();
 4         long now = 0;
 5 
 6         if (millis < 0) {
 7             throw new IllegalArgumentException("timeout value is negative");
 8         }
 9 
10         if (millis == 0) {
11             while (isAlive()) {
12                 wait(0);
13             }
14         } else {
15             while (isAlive()) {
16                 long delay = millis - now;
17                 if (delay <= 0) {
18                     break;
19                 }
20                 wait(delay);
21                 now = System.currentTimeMillis() - base;
22             }
23         }
24     }

实现需求的方案一:

 1 public class TestMain {
 2     public static void main(String[] args) throws InterruptedException {
 3         Thread T1 = new MyThread("T1");
 4         Thread T2 = new MyThread("T2");
 5         Thread T3 = new MyThread("T3");
 6 
 7         System.out.println("T1 start.");
 8         T1.start();
 9         T1.join();
10         System.out.println("T1 complete.");
11 
12         System.out.println("T2 start.");
13         T2.start();
14         T2.join();
15         System.out.println("T2 complete.");
16 
17         System.out.println("T3 start.");
18         T3.start();
19         T3.join();
20         System.out.println("T3 complete.");
21     }
22 }
23 
24 class MyThread extends Thread {
25     public MyThread(String name) {
26         setName(name);
27     }
28 
29     @Override
30     public void run() {
31         for (int i = 0; i < 5; i++) {
32             System.out.println(Thread.currentThread().getName() + ": " + i);
33             try {
34                 // do something...
35                 Thread.sleep(100);
36             } catch (InterruptedException e) {
37                 e.printStackTrace();
38             }
39         }
40     }
41 }

实现需求的方案二:

public class Test2Main {
    public static void main(String[] args) {
        final Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1...");
            }
        });
        final Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    T1.join();
                }catch (InterruptedException ex){
                    ex.printStackTrace();
                }
                System.out.println("T2...");
            }
        });
        final Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    T2.join();
                }catch (InterruptedException ex){
                    ex.printStackTrace();
                }
                System.out.println("T3...");
            }
        });

        T3.start();
        T2.start();
        T1.start();
    }
}

实现方案三:使用ReentrantLock来解决, 还有个state整数用来判断轮到谁执行了

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ABC {
    private static Lock lock = new ReentrantLock();//通过JDK5中的锁来保证线程的访问的互斥
    private static int state = 0;
    
    static class ThreadA extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                lock.lock();
                if (state % 3 == 0) {
                    System.out.print("A");
                    state++;
                    i++;
                }
                lock.unlock();
            }
        }
    }
    
    static class ThreadB extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                lock.lock();
                if (state % 3 == 1) {
                    System.out.print("B");
                    state++;
                    i++;
                }
                lock.unlock();
            }
        }
    }
    
    static class ThreadC extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                lock.lock();
                if (state % 3 == 2) {
                    System.out.print("C");
                    state++;
                    i++;
                }
                lock.unlock();
            }
        }
    }
    
    public static void main(String[] args) {
        new ThreadA().start();
        new ThreadB().start();
        new ThreadC().start();
    }
    
}

使用lock来保证只有一个线程在输出操作, 要保证了state不会被两个线程同时修改, 思路简单

实现方案四:还可以使用condition, condition的效率可能会更高一些, await会释放lock锁, condition的await和signal与object的wait和notify方法作用类似

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ABC2 {
    private static Lock lock = new ReentrantLock();
    private static int count = 0;
    private static Condition A = lock.newCondition();
    private static Condition B = lock.newCondition();
    private static Condition C = lock.newCondition();
    
    static class ThreadA extends Thread {

        @Override
        public void run() {
            lock.lock();
            try {
                for (int i = 0; i < 10; i++) {
                    while (count % 3 != 0)
                        A.await(); // 会释放lock锁
                    System.out.print("A");
                    count++;
                    B.signal(); // 唤醒相应线程
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
    }
    
    static class ThreadB extends Thread {

        @Override
        public void run() {
            lock.lock();
            try {
                for (int i = 0; i < 10; i++) {
                    while (count % 3 != 1)
                        B.await();
                    System.out.print("B");
                    count++;
                    C.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
    }
    
    static class ThreadC extends Thread {

        @Override
        public void run() {
            lock.lock();
            try {
                for (int i = 0; i < 10; i++) {
                    while (count % 3 != 2)
                        C.await();
                    System.out.println("C");
                    count++;
                    A.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
    }
    
    public static void main(String[] args) throws InterruptedException {
        new ThreadA().start();
        new ThreadB().start();
        ThreadC threadC = new ThreadC();
        threadC.start();
        threadC.join();
        System.out.println(count);
    }
}

实现方案五:使用信号量也可以, 这个思路最简单, 整个代码也比较简洁

import java.util.concurrent.Semaphore;

public class ABC3 {
    private static Semaphore A = new Semaphore(1);
    private static Semaphore B = new Semaphore(1);
    private static Semaphore C = new Semaphore(1);
    
    static class ThreadA extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    A.acquire();
                    System.out.print("A");
                    B.release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    static class ThreadB extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    B.acquire();
                    System.out.print("B");
                    C.release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    static class ThreadC extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    C.acquire();
                    System.out.println("C");
                    A.release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    public static void main(String[] args) throws InterruptedException {
        B.acquire(); C.acquire(); // 开始只有A可以获取, BC都不可以获取, 保证了A最先执行
        new ThreadA().start();
        new ThreadB().start();
        new ThreadC().start();
    }
}

注意:

lock是需要lock所有者去释放的, 即谁lock, 谁释放, 不可以跨线程, 会报java.lang.IllegalMonitorStateException;

semaphore是没有所有者的说法, 可以跨线程释放和获取.

方案三、四、五转自《[Java多线程]ABC三个线程顺序输出的问题