六種兩個線程交替打印(0A1B2C....)方法(目前自我認知6種)

一:六種方法

題目:編寫程序實現兩個線程一個線程輸出0、2、3…51,另外一個線程實現A...Z, a...z,(0A1B2C....49x50y51z)

很多地方都可以見到類似的筆試題或者面試題,今天小輝以自己認知的範圍內列舉了如下6種方法,如果你們還有其他的方法留言給小輝,小輝立馬學習改正。

1:使用synchronized & wait & notify

public static void useSynchronizedAndWaitAndNotify() {
        Object object = new Object();
        new Thread(() -> {
            synchronized (object) {
                for (int i = 0; i < 52; i++) {
                    try {
                        object.notify();
                        System.out.print(i);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (object) {
                for (int i = 0; i < 52; i++) {
                    try {
                        char ch = getChar(i);
                        object.notify();
                        System.out.print(ch);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

2:使用LinkedTransferQueue

使用put和transfer都可以,put是異步的,transfer是阻塞的。

public static void useTransferQueue() {
        LinkedTransferQueue queue1 = new LinkedTransferQueue();
        LinkedTransferQueue queue2 = new LinkedTransferQueue();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                try {
                    queue1.transfer(i);
//                    queue1.put(i);
                    System.out.print(queue2.take());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                try {
                    System.out.print(queue1.take());
                    char ch = getChar(i);
                    queue2.transfer(ch);
//                    queue2.put(ch);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

3:使用Lock & Condition

使用Lock鎖和Condition條件,這種寫法注意的是加鎖後一定要解鎖。使用這樣的寫法你寫多個線程交替循環執行也很容易實現,業務代碼放在signal和await前面,中間,後面有一些差別的哦。

public static void useLockAndCondition() {
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                lock.lock();
                try {
                    System.out.print(i);
                    condition2.signal();
                    condition1.await();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                lock.lock();
                char ch = getChar(i);
                try {
                    System.out.print(ch);
                    condition1.signal();
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();
    }

4:使用volatile & 循環

使用 volatile的常量,然後使用循環,由於使用循環去獲取flag的值,如果有一個業務中斷出問題了,其他哪個可能就會進入死循環,甚用。

private static volatile boolean flag = true;
    /**
     * 使用volatile & 循環
     */
    public static void useVolatile() {
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (flag) {
                    System.out.print(i);
                    flag = false;
                }
                while (!flag) {
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (!flag) {
                    char ch = getChar(i);
                    System.out.print(ch);
                    flag = true;
                }
                while (flag) {
                }
            }
        }).start();
    }

5:使用Atomic(CAS) & 循環

使用Atomic和循環,Atomic看源碼就是使用了CAS算法進行自加自減操作的,這個也要保證業務代碼的異常捕獲處理,一個線程中斷,另外一個線程也應該中斷,否則又可以背鍋了。

  private static AtomicInteger atomic = new AtomicInteger(0);
    /**
     * 使用Atomic(CAS) & 循環
     */
    public static void useAtomic() {
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (atomic.get() % 2 == 0) {
                    System.out.print(i);
                    atomic.incrementAndGet();
                }
                while (atomic.get() % 2 != 0) {
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (atomic.get() % 2 != 0) {
                    char ch = getChar(i);
                    System.out.print(ch);
                    atomic.incrementAndGet();
                }
                while (atomic.get() % 2 == 0) {
                }
            }
        }).start();
    }

6:使用LockSupport

LockSupport是一個線程阻塞工具類,所有的方法都是靜態方法,可以讓線程在任意位置阻塞,當然阻塞之後肯定得有喚醒的方法。

 private static Thread t1;

    private static Thread t2;
    /**
     * 使用LockSupport
     */
    public static void uselockSupport() {
        t1 = new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                System.out.print(i);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });

        t2 = new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                char ch = getChar(i);
                LockSupport.park();
                System.out.print(ch);
                LockSupport.unpark(t1);
            }
        });
        t1.start();
        t2.start();
    }

二、性能簡單比較

小輝寫了6個方法,但是也不知道那個方法是性能好的,所以小輝偷偷寫了一個main函數運行了一波結果如下:

 由於數據不是很大所以差別不明顯,但是線程安全方面考慮第一種方法是不錯的,其他的要使用哪種方法就看你比較後自己的選擇了。

完整代碼如下:

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yh128
 * @version 1.0.0
 * @ClassName Threads.java
 * @Description 兩線程交替打印數據
 * @Param
 * @createTime 2019年12月25日 15:09:00
 */
public class Threads {


    private static String timeStr = "耗時:%s ms";

    private static String title = "\n--------------[%s] ";

    /**
     * 使用synchronized & wait & notify
     */
    public static void useSynchronizedAndWaitAndNotify() {
        Object object = new Object();
        new Thread(() -> {
            synchronized (object) {
                for (int i = 0; i < 52; i++) {
                    try {
                        object.notify();
                        System.out.print(i);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (object) {
                for (int i = 0; i < 52; i++) {
                    try {
                        char ch = getChar(i);
                        object.notify();
                        System.out.print(ch);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    /**
     * 使用LinkedTransferQueue
     */
    public static void useTransferQueue() {
        LinkedTransferQueue queue1 = new LinkedTransferQueue();
        LinkedTransferQueue queue2 = new LinkedTransferQueue();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                try {
                    queue1.transfer(i);
//                    queue1.put(i);
                    System.out.print(queue2.take());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                try {
                    System.out.print(queue1.take());
                    char ch = getChar(i);
                    queue2.transfer(ch);
//                    queue2.put(ch);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 使用Lock & Condition
     */
    public static void useLockAndCondition() {
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                lock.lock();
                try {
                    System.out.print(i);
                    condition2.signal();
                    condition1.await();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                lock.lock();
                char ch = getChar(i);
                try {
                    System.out.print(ch);
                    condition1.signal();
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();
    }

    public static volatile boolean flag = true;

    /**
     * 使用volatile & 循環
     */
    public static void useVolatile() {
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (flag) {
                    System.out.print(i);
                    flag = false;
                }
                while (!flag) {
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (!flag) {
                    char ch = getChar(i);
                    System.out.print(ch);
                    flag = true;
                }
                while (flag) {
                }
            }
        }).start();
    }

    private static AtomicInteger atomic = new AtomicInteger(0);

    /**
     * 使用Atomic(CAS) & 循環
     */
    public static void useAtomic() {
        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (atomic.get() % 2 == 0) {
                    System.out.print(i);
                    atomic.incrementAndGet();
                }
                while (atomic.get() % 2 != 0) {
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                if (atomic.get() % 2 != 0) {
                    char ch = getChar(i);
                    System.out.print(ch);
                    atomic.incrementAndGet();
                }
                while (atomic.get() % 2 == 0) {
                }
            }
        }).start();
    }


    private static Thread t1;

    private static Thread t2;

    /**
     * 使用LockSupport
     */
    public static void uselockSupport() {
        t1 = new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                System.out.print(i);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });

        t2 = new Thread(() -> {
            for (int i = 0; i < 52; i++) {
                char ch = getChar(i);
                LockSupport.park();
                System.out.print(ch);
                LockSupport.unpark(t1);
            }
        });
        t1.start();
        t2.start();
    }

    public static void main(String[] args) throws InterruptedException {
        // 第一個是包含初始化時間所以不精確不算在比較中
        long start = System.currentTimeMillis();
        System.out.println(String.format(title, "使用synchronized & wait & notify(包含了啓動時間所以再開啓一個來比較)"));
        useSynchronizedAndWaitAndNotify();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();

        System.out.println(String.format(title, "使用synchronized & wait & notify"));
        useSynchronizedAndWaitAndNotify();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();

        System.out.println(String.format(title, "使用LinkedTransferQueue"));
        useTransferQueue();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();


        System.out.println(String.format(title, "使用Lock & Condition"));
        useLockAndCondition();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();

        System.out.println(String.format(title, "使用volatile & 循環"));
        useVolatile();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();

        System.out.println(String.format(title, "使用Atomic(CAS) & 循環"));
        useAtomic();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        start = System.currentTimeMillis();

        System.out.println(String.format(title, "使用LockSupport"));
        uselockSupport();
        Thread.sleep(500);
        System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
        // 關閉應用
        System.exit(-1);
    }

    /**
     * 獲取字母
     *
     * @param i
     * @return
     */
    private static char getChar(int i) {
        if (i >= 26) {
            return (char) (71 + i);
        }
        return (char) (65 + i);
    }
}

這是小輝發現的6種方法有問題可以留言或者評論哦,歡迎你的技術性騷擾哦\(^o^)/。

 

歡迎關注哲思小輝

 

發佈了43 篇原創文章 · 獲贊 60 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章