1. 程式人生 > >實現多個執行緒依次執行

實現多個執行緒依次執行

概述

執行緒間的同步是開發中可能遇到的,有時候我們需要控制它們的執行順序來滿足我們的需求,今天總結一下幾種實現執行緒同步的方式。

方式一(Thread的join方法)

/**
 * 實現執行緒順序執行方式1:
 * 使用Thread物件的join方法,該方法的意思是當呼叫join方法的執行緒
 * 執行完畢後,當前執行緒才執行
 * 該方式只能執行一輪列印,不能實現類似ABCABC的多輪列印,
 */
public class TestThread {
    public static void main(String[] args) {
        final Alternate alternate = new Alternate();
        final Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                alternate.printA();
            }
        },"A");
        a.start();
        final Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    a.join();
                    alternate.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B");
        b.start();
        Thread c = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    b.join();
                    alternate.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C");
        c.start();
    }

    static class Alternate{
        public void printA(){
            for (int i = 1; i <= 3 ; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
        public void printB(){
            for (int i = 1; i <= 3 ; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
        public void printC(){
            for (int i = 1; i <= 3 ; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

方式二(等待喚醒機制Lock)

/**
 * 實現執行緒順序執行方式2:
 * 使用同步鎖的等待喚醒機制
 * 可以實現多輪列印
 */
public class TestThread2 {
    public static void main(String[] args) {
        final Alternate alternate = new Alternate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printA();
                }
            }
        }, "A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printB();
                }
            }
        }, "B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printC();
                }
            }
        }, "C").start();
    }

    static class Alternate {
        private int num = 1;
        private Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Condition condition3 = lock.newCondition();

        public void printA() {
            lock.lock();
            try {
                if (num != 1) {
                    condition1.await();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 2;
                condition2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void printB() {
            lock.lock();
            try {
                if (num != 2) {
                    condition2.await();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 3;
                condition3.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void printC() {
            lock.lock();
            try {
                if (num != 3) {
                    condition3.await();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 3;
                condition1.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

方式三(等待喚醒機制synchronized)

/**
 * 實現執行緒順序執行方式3:
 * 同步方法結合wait和notify
 * 可以實現多輪列印
 */
public class TestThread3 {
    public static void main(String[] args) {
        final Alternate alternate = new Alternate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printA();
                }
            }
        }, "A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printB();
                }
            }
        }, "B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 2; i++) {
                    alternate.printC();
                }
            }
        }, "C").start();
    }

    static class Alternate {
        private int num = 1;
        public synchronized void printA() {
            try {
                if (num != 1) {
                    wait();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 2;
                notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public synchronized void printB() {
            try {
                if (num != 2) {
                    wait();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 3;
                notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public synchronized void printC() {
            try {
                if (num != 3) {
                    wait();
                }
                for (int i = 1; i <= 3; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
                num = 3;
                notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

方式四(CountDownLatch)

/**
 *CountDownLatch:在完成一組正在其他執行緒中執行的操作之前,
 * 它允許一個或多個執行緒一直等待。
 *重點方法:
 * await():當CountDownLatch的計數為0之前,await所在的執行緒才是掛起的
 */
public class TestThread4 {
    //保證A和B之前的順序
    static CountDownLatch latch = new CountDownLatch(1);
    //保證B和C之間的關係
    static CountDownLatch latch2 = new CountDownLatch(1);
    public static void main(String[] args) {
        final Alternate alternate = new Alternate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    alternate.printA();
                }
            }
        }, "A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    alternate.printB();
                }
            }
        }, "B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    alternate.printC();
                }
            }
        }, "C").start();
    }

    static class Alternate {
        public void printA() {
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
            latch.countDown();
        }

        public void printB() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
            latch2.countDown();
        }

        public void printC() {
            try {
                latch2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

引出問題

在第三種方式中,我們可以看到wait方法是放在同步方法裡的,那麼為什麼要放在同步方法裡,加入我們的方法不是同步的,會丟擲下面的異常

Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.lang.Object.notifyAll(Native Method)
	at com.example.client.TestThread3$Alternate.printA(TestThread3.java:48)
	at com.example.client.TestThread3$1.run(TestThread3.java:15)
	at java.lang.Thread.run(Thread.java:748)