【马士兵公开课】面试题。用两个线程,一个输出字母,一个输出数字,交替输出。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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章