兩種方式實現線程通信:三個線程交替打印AABBCC

多線程之間是搶佔資源的,使用線程通信可以達到線程按序執行的目的

線程共享資源類, 首先創建一個資源類, 包含三個打印的方法以及首次打印的字符串
多個線程訪問,方法加synchronized同步鎖

class Resource {

    String currentPrint = "AA"; // 初始打印,打印之後賦值爲下一個要打印的內容

    /**
     * 打印AA
     * @param next 下一個要打印的字符串
     * @throws InterruptedException
     */
    public synchronized void printAA(String next) throws InterruptedException {
      	// 不是要打印的內容, 阻塞線程
        while (!Objects.equals(currentPrint, "AA")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": AA");
        }
        currentPrint= next;
        // 喚醒其他線程
        notifyAll();
    }

    /**
     * 打印BB
     * @param next 下一個要打印的字符串
     * @throws InterruptedException
     */
    public synchronized void printBB(String next) throws InterruptedException {
      	// 不是要打印的內容, 阻塞線程
        while (!Objects.equals(currentPrint, "BB")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": BB");
        }
        currentPrint= next;
        // 喚醒其他線程
        notifyAll();
    }

    /**
     * 打印CC
     * @throws InterruptedException
     */
    public synchronized void printCC() throws InterruptedException {
    	// 不是要打印的內容, 阻塞線程
        while (!Objects.equals(currentPrint, "CC")) {
            wait();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": CC");
        }
    }
}

創建線程執行程序進行交替打印

public class AABBCC {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 創建一個最大長度爲3的線程池
            threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0,
                    TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            Resource resource = new Resource();
            // 將要執行的任務插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{try { resource.printAA("BB"); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printBB("CC"); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printCC();} catch (InterruptedException e) {e.printStackTrace();}});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 關閉線程池
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}

執行結果
在這裏插入圖片描述
資源類冗餘代碼進行優化

    /**
     * 打印字符串
     * @param currentPrintParam 當前
     * @param next 下一個
     * @param times 打印次數
     * @throws InterruptedException
     */
    public synchronized void printStr(String currentPrintParam,String next, int times) throws InterruptedException {
      	// 不是要打印的內容, 阻塞線程
        while (!Objects.equals(currentPrint, currentPrintParam)) {
            wait();
        }
        for (int i = 0; i < times; i++) {
            System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
        }
        currentPrint = next;
        // 喚醒其他線程
        notifyAll();
    }

調用方法替換

            Resource resource = new Resource();
            // 將要執行的任務插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{try { resource.printStr("AA", "BB", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printStr("BB", "CC", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
            list.add(()->{try { resource.printStr("CC", "", 3);} catch (InterruptedException e) {e.printStackTrace();}});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }

執行結果
在這裏插入圖片描述
JDK5在java.util.concurrent包下提供了Lock併發鎖
資源類代碼新增

    Lock lock = new ReentrantLock(); // 併發鎖
    Condition aCondition = lock.newCondition();
    Condition bCondition = lock.newCondition();
    Condition cCondition = lock.newCondition();

    /**
     * 打印字符串
     * @param currentPrintParam 當前線程打印
     * @param nextPrint 下一個線程打印
     * @param currentCondition 當前線程
     * @param nextCondition 下一個線程
     * @param times 打印次數
     * @throws InterruptedException
     */
    public void lockPrintStr(String currentPrintParam, String nextPrint, Condition currentCondition, Condition nextCondition, int times) {
        lock.lock();
        try {
            // 不是要打印的內容 阻塞線程
            while (!Objects.equals(currentPrint, currentPrintParam)) {
                currentCondition.await();
            }
            for (int i = 0; i < times; i++) {
                System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
            }
            currentPrint = nextPrint;
            // 喚醒下一個打印條件的線程
            if (!Objects.isNull(nextCondition)) {
                nextCondition.signal();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 釋放鎖
            if (lock != null) {
                lock.unlock();
            }
        }
    }

調用新增的方法

            Resource resource = new Resource();
            // 將要執行的任務插入到集合中
            List<Runnable> list = new ArrayList<>();
            list.add(()->{resource.lockPrintStr("AA", "BB", resource.aCondition, resource.bCondition, 3);});
            list.add(()->{resource.lockPrintStr("BB", "CC", resource.bCondition, resource.cCondition, 3);});
            list.add(()->{resource.lockPrintStr("CC", "", resource.cCondition, null, 3);});
            for (int i = 0; i < list.size(); i++) {
                threadPoolExecutor.execute(list.get(i));
            }

執行結果
在這裏插入圖片描述
多次執行的結果中可以看出,由三個線程交替打印AABBCC

總結:

  1. 線程通信其中兩種方式 Object類下wait()、notify()/notifyAll(),以及JDK5以後提供的Lock下Condition內部類的await()、signal()方法。
  2. 使用wait/notifyAll進行線程通信喚醒喚醒了所有線程,增加了上下文的切換時間,使用await/signal可以實現精準喚醒,java集合框架中的隊列就採用了後者實現線程通信
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章