1. 程式人生 > 實用技巧 >多執行緒案例

多執行緒案例

一、執行緒的交替列印問題

  兩個執行緒交替列印字母和數字,其中執行緒1列印數字,執行緒2列印字母,列印形式如下:

    12A34B56C......

  方法一,使用ReentrantLock實現,程式如下:

public class ThreadTest02 {
    private static  Lock lock = new ReentrantLock();
    private static  Condition conditionA = lock.newCondition();
    private static  Condition conditionB = lock.newCondition();
    private static volatile boolean flag = true;

    public void printNum(int i) throws InterruptedException {
        lock.lock();
        while (!flag){
            conditionA.await();
        }
        System.out.print(2 * i - 1);
        System.out.print(2 * i);
        flag = false;
        conditionB.signal();
        lock.unlock();
    }

    public  void printChar(int i) throws InterruptedException {
        lock.lock();
        while (flag){
            conditionB.await();
        }
        System.out.print(Character.toChars(i - 1 + 'A'));
        flag  = true;
        conditionA.signal();
        lock.unlock();
    }

    public static void main(String[] args) {
        ThreadTest02 test02 = new ThreadTest02();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 27; i++) {
                    try {
                        test02.printNum(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 27; i++) {
                    try {
                        test02.printChar(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

 condition也是基於AQS實現的,內部維護了一個等待佇列,所有呼叫condition.await方法的執行緒會加入到等待佇列中,並且執行緒狀態轉換為等待狀態。當呼叫condition.await()方法後會使得當前獲取lock的執行緒進入到等待佇列,如果該執行緒能夠從await()方法返回的話一定是該執行緒獲取了與condition相關聯的lock。

  方法二,使用synchronized關鍵字解決:

public class ThreadTest03 {
    private static Object object = new Object();
    private static volatile boolean flag = true;

    public void printNum(int i) throws InterruptedException {
        synchronized (object){
            if(!flag){
                object.wait();
            }
            System.out.print(i * 2 - 1);
            System.out.print(i * 2);
            flag = false;
            object.notify();
        }
    }

    public void printChar(int i) throws InterruptedException {
        synchronized (object){
            if(flag){
                object.wait();
            }
            System.out.print(Character.toChars(i - 1 + 'A'));
            flag = true;
            object.notify();
        }
    }

    public static void main(String[] args) {
        ThreadTest03 test03 = new ThreadTest03();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 27; i++) {
                    try {
                        test03.printNum(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 27; i++) {
                    try {
                        test03.printChar(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

二、生產者和消費者問題

  生產者-消費者問題,實際上是指程式中包含兩類執行緒,一種是用於生產資料的生產者執行緒,另一種是用於消費資料的消費者執行緒,生產者生產的資料放置於一個有固定容量的緩衝區當中,消費者從這個緩衝區中拿出資料消費。主要解決兩個問題:

  1. 如果共享資料區已滿的話,阻塞生產者繼續生產資料放置入內;
  2. 如果共享資料區為空的話,阻塞消費者繼續消費資料;

程式碼如下:

package com.practice.threadDemo;

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

public class PSThreadTest04 {
    private static Lock lock = new ReentrantLock();
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static volatile int free_size = 10; //緩衝區可以容納的大小
    
    public static void main(String[] args) {
        PSThreadTest04 test04 = new PSThreadTest04();
        for (int k = 0; k < 5; k++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        try {
                            test04.new Producer().produce();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }

        for (int k = 0; k < 5; k++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        try {
                            test04.new Consumer().consume();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }

    /**方法一 :ReentrantLock的方式實現 **/
    class Producer {
        public void produce() throws InterruptedException {
            lock.lock();
            while (free_size <= 0) {
                conditionA.await();
            }
            System.out.println("生產者生產了一件產品");
            free_size--;
            conditionB.signal();
            lock.unlock();
        }
    }

    class Consumer {
        public void consume() throws InterruptedException {
            lock.lock();
            while (free_size == 10) {
                conditionB.await();
            }
            System.out.println("消費者消費了一件產品");
            free_size++;
            conditionA.signal();
            lock.unlock();
        }
    }

}

  此處也提供了第二種方法去實現:

package com.practice.threadDemo;

public class PSThreadTest04 {

    private static volatile int free_size = 10; //緩衝區可以容納的大小
    private static volatile Object object = new Object();

    public static void main(String[] args) {
        PSThreadTest04 test04 = new PSThreadTest04();
        for (int k = 0; k < 5; k++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        try {
                            test04.new Producer().produce();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }

        for (int k = 0; k < 5; k++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        try {
                            test04.new Consumer().consume();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }


    /**方法二 :synchronized的方式實現 **/
    class Producer{
        public void produce() throws InterruptedException {
            synchronized (object){
                while(free_size <= 0){
                    object.wait();
                }
                System.out.println("生產者生產了一件產品");
                free_size--;
                object.notify();
            }
        }
    }

    class Consumer{
        public void consume() throws InterruptedException {
            synchronized (object){
                while(free_size >= 10){
                    object.wait();
                }
                System.out.println("消費者消費了一件產品");
                free_size++;
                object.notify();
            }
        }
    }
}