如何讓兩個線程交替打印數字

問題

如何讓兩個線程交替打印1-100的數字?廢話很少說,直接上代碼:java

synchronized鎖+AtomicInteger

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class StrangePrinter {

    private int max;
    private AtomicInteger status = new AtomicInteger(1); // AtomicInteger保證可見性,也能夠用volatile

    public StrangePrinter(int max) {
        this.max = max;
    }

    public static void main(String[] args) {
        StrangePrinter strangePrinter = new StrangePrinter(100);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(strangePrinter.new MyPrinter("Print1", 0));
        executorService.submit(strangePrinter.new MyPrinter("Print2", 1));
        executorService.shutdown();
    }

    class MyPrinter implements Runnable {
        private String name;
        private int type; // 打印的類型,0:表明打印奇數,1:表明打印偶數

        public MyPrinter(String name, int type) {
            this.name = name;
            this.type = type;
        }

        @Override
        public void run() {
            if (type == 1) {
                while (status.get() <= max) {
                    synchronized (StrangePrinter.class) { // 加鎖,保證下面的操做是一個原子操做
                        // 打印偶數
                        if (status.get() <= max && status.get() % 2 == 0) { // 打印偶數
                            System.out.println(name + " - " + status.getAndIncrement());
                        }
                    }
                }
            } else {
                while (status.get() <= max) {
                    synchronized (StrangePrinter.class) { // 加鎖
                        // 打印奇數
                        if (status.get() <= max && status.get() % 2 != 0) { // 打印奇數
                            System.out.println(name + " - " + status.getAndIncrement());
                        }
                    }
                }
            }
        }
    }
}

複製代碼

這裏須要注意兩點:bash

  1. 用AtomicInteger保證多線程數據可見性。
  2. 不要以爲synchronized加鎖是多餘的,若是沒有加鎖,線程1和線程2就可能出現不是交替打印的狀況。若是沒有加鎖,設想線程1打印完了一個奇數後,線程2去打印下一個偶數,當執行完status.getAndIncrement()後,此時status又是奇數了,當此時cpu將線程2掛起,調度線程1,就會出現線程2還沒來得及打印偶數,線程1就已經打印了下一個奇數的狀況。就不符合題目要求了。所以這裏加鎖是必須的,保證代碼塊中的是一個原子操做。

使用Object的wait和notify實現

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class StrangePrinter2 {

    Object odd = new Object(); // 奇數條件鎖
    Object even = new Object(); // 偶數條件鎖
    private int max;
    private AtomicInteger status = new AtomicInteger(1); // AtomicInteger保證可見性,也能夠用volatile

    public StrangePrinter2(int max) {
        this.max = max;
    }

    public static void main(String[] args) {
        StrangePrinter2 strangePrinter = new StrangePrinter2(100);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(strangePrinter.new MyPrinter("偶數Printer", 0));
        executorService.submit(strangePrinter.new MyPrinter("奇數Printer", 1));
        executorService.shutdown();
    }

    class MyPrinter implements Runnable {
        private String name;
        private int type; // 打印的類型,0:表明打印奇數,1:表明打印偶數

        public MyPrinter(String name, int type) {
            this.name = name;
            this.type = type;
        }

        @Override
        public void run() {
            if (type == 1) {
                while (status.get() <= max) { // 打印奇數
                    if (status.get() % 2 == 0) { // 若是當前爲偶數,則等待
                        synchronized (odd) {
                            try {
                                odd.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    } else {
                        System.out.println(name + " - " + status.getAndIncrement()); // 打印奇數
                        synchronized (even) { // 通知偶數打印線程
                            even.notify();
                        }
                    }
                }
            } else {
                while (status.get() <= max) { // 打印偶數
                    if (status.get() % 2 != 0) { // 若是當前爲奇數,則等待
                        synchronized (even) {
                            try {
                                even.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    } else {
                        System.out.println(name + " - " + status.getAndIncrement()); // 打印偶數
                        synchronized (odd) { // 通知奇數打印線程
                            odd.notify();
                        }
                    }
                }
            }
        }
    }
}

複製代碼

使用ReentrantLock+Condition實現

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class StrangePrinter3 {

    private int max;
    private AtomicInteger status = new AtomicInteger(1); // AtomicInteger保證可見性,也能夠用volatile
    private ReentrantLock lock = new ReentrantLock();
    private Condition odd = lock.newCondition();
    private Condition even = lock.newCondition();

    public StrangePrinter3(int max) {
        this.max = max;
    }

    public static void main(String[] args) {
        StrangePrinter3 strangePrinter = new StrangePrinter3(100);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(strangePrinter.new MyPrinter("偶數Printer", 0));
        executorService.submit(strangePrinter.new MyPrinter("奇數Printer", 1));
        executorService.shutdown();
    }

    class MyPrinter implements Runnable {
        private String name;
        private int type; // 打印的類型,0:表明打印奇數,1:表明打印偶數

        public MyPrinter(String name, int type) {
            this.name = name;
            this.type = type;
        }

        @Override
        public void run() {
            if (type == 1) {
                while (status.get() <= max) { // 打印奇數
                    lock.lock();
                    try {
                        if (status.get() % 2 == 0) {
                            odd.await();
                        }
                        if (status.get() <= max) {
                            System.out.println(name + " - " + status.getAndIncrement()); // 打印奇數
                        }
                        even.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            } else {
                while (status.get() <= max) { // 打印偶數
                    lock.lock();
                    try {
                        if (status.get() % 2 != 0) {
                            even.await();
                        }
                        if (status.get() <= max) {
                            System.out.println(name + " - " + status.getAndIncrement()); // 打印偶數
                        }
                        odd.signal();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    }
}

複製代碼

這裏的實現思路其實和使用Object的wait和notify機制差很少。markdown

經過flag標識實現

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class StrangePrinter4 {

    private int max;
    private AtomicInteger status = new AtomicInteger(1); // AtomicInteger保證可見性,也能夠用volatile
    private boolean oddFlag = true;

    public StrangePrinter4(int max) {
        this.max = max;
    }

    public static void main(String[] args) {
        StrangePrinter4 strangePrinter = new StrangePrinter4(100);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(strangePrinter.new MyPrinter("偶數Printer", 0));
        executorService.submit(strangePrinter.new MyPrinter("奇數Printer", 1));
        executorService.shutdown();
    }

    class MyPrinter implements Runnable {
        private String name;
        private int type; // 打印的類型,0:表明打印奇數,1:表明打印偶數

        public MyPrinter(String name, int type) {
            this.name = name;
            this.type = type;
        }

        @Override
        public void run() {
            if (type == 1) {
                while (status.get() <= max) { // 打印奇數
                    if (oddFlag) {
                        System.out.println(name + " - " + status.getAndIncrement()); // 打印奇數
                        oddFlag = !oddFlag;
                    }
                }
            } else {
                while (status.get() <= max) { // 打印偶數
                    if (!oddFlag) {
                        System.out.println(name + " - " + status.getAndIncrement()); // 打印奇數
                        oddFlag = !oddFlag;
                    }
                }
            }
        }
    }
}

複製代碼

這是最簡單最高效的實現方式,由於不須要加鎖。比前面兩種實現方式都要好一些。多線程

相關文章
相關標籤/搜索