【馬士兵公開課】面試題。用兩個線程,一個輸出字母,一個輸出數字,交替輸出。1A2B3C4D5E6F7G

 

package com.zillion.springcloudeurekaserver;

import org.junit.Test;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 馬士兵公開課
 * 一個面試題:
 * 用兩個線程,一個輸出字母,一個輸出數字,交替輸出。1A2B3C4D5E6F7G
 * https://ke.qq.com/course/399017?flowToken=1014847&taid=3803502778652329&dialog=1
 */
public class ThreadTest1 {
    char[] aI = "1234567".toCharArray();
    char[] aC = "ABCDEFG".toCharArray();

    static Thread t1 = null, t2 = null;

    /**
     * 運行結果1A2B3C4D5E6F7G
     */
    @Test
    public void testLockSupport() {
        t1 = new Thread(new Thread(() -> {
            for (char c : aI) {
                System.out.print(c);
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        }), "t1");
        t2 = new Thread(new Thread(() -> {
            for (char c : aC) {
                LockSupport.park();
                System.out.print(c);
                LockSupport.unpark(t1);
            }
        }), "t2");
        t1.start();
        t2.start();
    }

    Object object = new Object();

    /**
     * 如果想用sychronized,必須是對某個對象加鎖,而不是包含的代碼
     * notify:叫醒鎖定這個對象的等待隊列裏面的任意一個線程,notifyAll是叫醒所有
     * wait:本來還在運行那。wait後就進入等待隊列了,同時釋放這把鎖
     */
    @Test
    public void testSyncWaitNofiy() {
        new Thread(() -> {
            synchronized (object) {
                for (char c : aI) {
                    System.out.print(c);
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                object.notify();//必須,否則無法終止程序

            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (object) {
                for (char c : aC) {
                    System.out.print(c);
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                object.notify();//必須,否則無法終止程序
            }
        }, "t2").start();
    }

    private static volatile boolean t2Started = false;

    /**
     * 在testSyncWaitNofiy的基礎上,控制第一個線程先運行,
     * 方法一,自旋
     * 爲什麼調用了start() 。還不一定就是他先運行。因爲start()後,線程是進入cpu的等待裏面,準備好狀態。什麼時候調用是cpu裏面調度的事情了。
     */
    @Test
    public void testSyncWaitNofiy1() {
        new Thread(() -> {
            synchronized (object) {
                while (!t2Started) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (char c : aI) {
                    System.out.print(c);
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                object.notify();//必須,否則無法終止程序

            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (object) {
                for (char c : aC) {
                    System.out.print(c);
                    t2Started = true;
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                object.notify();//必須,否則無法終止程序
            }
        }, "t2").start();
    }

    private static CountDownLatch latch = new CountDownLatch(1);

    /**
     * 在testSyncWaitNofiy的基礎上,控制第一個線程先運行,
     * 方法二,CountDownLatch
     * CountDownLatch(int count) //實例化一個倒計數器,count指定計數個數
     * countDown() // 計數減一
     * await() //等待,當計數減到0時,所有線程並行執行
     */
    @Test
    public void testSyncWaitNofiy2() {
        new Thread(() -> {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object) {
                for (char c : aI) {
                    System.out.print(c);
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                object.notify();//必須,否則無法終止程序

            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (object) {
                for (char c : aC) {
                    System.out.print(c);
                    latch.countDown();
                    try {
                        object.notify();
                        object.wait();//讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                object.notify();//必須,否則無法終止程序
            }
        }, "t2").start();
    }

    enum ReadyToRun {T1, T2}

    static volatile ReadyToRun r = ReadyToRun.T1;

    /**
     * 模擬自旋鎖,自旋就是原地打轉
     * 沒輪到它的時候,不放棄cpu。自旋鎖有個好處是不經過操作系統
     * 除了自旋鎖,擴展信息:
     * 1.偏向鎖:只記錄線程id
     * 2.自旋鎖:線程不放棄cpu。線程可以用wait放棄cpu,進入等待隊列裏面。應用場景。這段程序速度特別快。空轉幾遍就輪到他了。
     * 3.重量級鎖:併發量大,線程多
     * <p>
     * 內核態,用戶態。正常情況下,申請一把鎖是從用戶態到內核態(操作系統內核)。效率偏低。
     */
    @Test
    public void testCas() {
        new Thread(() -> {
            for (char c : aI) {
                while (r != ReadyToRun.T1) {
                }
                System.out.print(c);
                r = ReadyToRun.T2;
            }
        }, "t1").start();
        new Thread(() -> {
            for (char c : aC) {
                while (r != ReadyToRun.T2) {
                }
                System.out.print(c);
                r = ReadyToRun.T1;
            }
        }, "t2").start();
    }

    static BlockingQueue<String> q1 = new ArrayBlockingQueue<>(1);//blockingQueue線程安全
    static BlockingQueue<String> q2 = new ArrayBlockingQueue<>(1);

    /**
     * 開拓思路用
     */
    @Test
    public void testBlockingQueue() {
        new Thread(() -> {
            for (char c : aI) {
                System.out.print(c);
                try {
                    q1.put("ok");//如果隊列滿了,一直阻塞,直到隊列不滿了或者線程被中斷-->阻塞
                    q2.take();//如果隊列空了,一直阻塞,直到隊列不爲空或者線程被中斷-->阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
        new Thread(() -> {
            for (char c : aC) {
                try {
                    q1.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print(c);
                try {
                    q2.put("ok");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t2").start();
    }

    /**
     * 管道流線程安全,read,write都是阻塞的
     * 效率低
     * 運行結果:1A2B3C4D5E6F7G1A2B3C4D5E16FA72GB3C4D5E6F7G
     */
    public void testPipedStream() throws Exception {
        //1.建立管道,管道連接
        PipedInputStream input1 = new PipedInputStream();
        PipedInputStream input2 = new PipedInputStream();
        PipedOutputStream output1 = new PipedOutputStream();
        PipedOutputStream output2 = new PipedOutputStream();
        input1.connect(output1);
        input2.connect(output2);

        String msg = "Your Turn";
        new Thread(() -> {
            byte[] buffer = new byte[9];
            try {
                for (char c : aI) {
                    input1.read(buffer);//讀一個字符
                    if (new String(buffer).equals(msg)) {
                        System.out.print(c);
                    }
                    output1.write(msg.getBytes());//寫入
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }, "t1").start();
        new Thread(() -> {
            byte[] buffer = new byte[9];
            try {
                for (char c : aC) {
                    System.out.print(c);
                    output2.write(msg.getBytes());//寫入
                    input2.read(buffer);
                    if (new String(buffer).equals(msg)) {
                        continue;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }

    /**
     * 一把鎖。不同條件下面的鎖定。這個的優點是能準確通知哪個線程運行。
     * 如果輪到自己。是從wait方法後面執行。
     */
    @Test
    public void testLockCondition() {
        Lock lock = new ReentrantLock();
        Condition conditionT1 = lock.newCondition();
        Condition conditionT2 = lock.newCondition();
        new Thread(() -> {
            try {
                lock.lock();
                for (char c : aI) {
                    System.out.print(c);
                    conditionT2.signal();
                    conditionT1.await();
                }
                conditionT2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1").start();
        new Thread(() -> {
            try {
                lock.lock();
                for (char c : aC) {
                    System.out.print(c);
                    conditionT1.signal();
                    conditionT2.await();
                }
                conditionT1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }
}

 

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