面试题:

用两个线程,一个输出字母,一个输出数字,交替输出1A2B3C…26Z

方法一:wait_notify

code

public class OutputByTurns2 {
    public static void main(String[] args) throws Exception {
        final Object o = new Object();
        int a = (int) 'a';
        CountDownLatch countDownLatch = new CountDownLatch(1); // 控制执行顺序


        new Thread(() -> {
            // method
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o) {
                for (int i = 0; i < 26; i++) {

                    System.out.println((char) (a + i));
                    try {
                        o.notify();
                        o.wait();// 让出锁 持有该对象锁的线程去wait()
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "字母").start();

        new Thread(() -> {
            // method
            synchronized (o) {
                countDownLatch.countDown();
                for (int i = 0; i < 26; i++) {
                    System.out.println(i + 1);
                    try {
                        o.notify(); // 随机唤醒一个 wait 线程
                        o.wait();// 让出锁 持有该对象锁的线程去wait()
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify(); // ⚠️ 不然程序停止不了,一直互相通知
            }
        }, "数字").start();
    }
}

方法二:LockSupport

code

public class OutputByTurns {
    static Thread t1,t2 = null;

    public static void main(String[] args) {
        int a  = (int) 'a';
        
        t1 = new Thread(() -> {
            // method
            for (int i=0;i<26;i++) {
                System.out.println(i+1);
                LockSupport.unpark(t2); // 叫醒t2
                LockSupport.park(); // t1 阻塞
            }
        }, "t1");

        t2 = new Thread(() -> {
            // method
            for (int i=0;i<26;i++) {
                LockSupport.park(); // t2 阻塞
                System.out.println((char) (a+i));
                LockSupport.unpark(t1); // 叫醒t1

            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

方法三:ReentrantLock

资源类写法 🤔

code

class ShareData {
    private int flag = 1; // 0:打印字母 1:打印数字
    private Lock lock = new ReentrantLock();
    private Condition conditionNumber = lock.newCondition();
    private Condition conditionLetter = lock.newCondition();

    public void printNumber(int number) {
        lock.lock();
        try {
            // 1 判断
            while (flag != 1) {
                conditionNumber.await();
            }
            // 2 干活
            System.out.println(number);
            // 3 通知
            flag = 0;
            conditionLetter.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printLetter(char ch) {
        lock.lock();
        try {
            // 1 判断
            while (flag != 0) {
                conditionLetter.await();
            }
            // 2 干活
            System.out.println(ch);
            // 3 通知
            flag = 1;
            conditionNumber.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class OutputByTurns3_2 {
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            // method
            for (int i = 0; i < 26; i++) {
                shareData.printNumber(i+1);
            }
        }, "打印数字").start();

        new Thread(() -> {
            // method
            for (int i = 0; i < 26; i++) {
                shareData.printLetter((char)('a'+i));
            }
        }, "打印字母").start();
    }
}	

CountDownLatch 控制顺序

code

public class OutputByTurns3 {
    public static void main(String[] args) {
        int a = (int) 'a';
        Lock lock = new ReentrantLock();
        CountDownLatch countDownLatch = new CountDownLatch(1); // 控制执行顺序
        // ⚠️ ReentrantLock可以指定唤醒哪一个队列中的线程
        Condition conditionNumber = lock.newCondition(); // 数字条件队列
        Condition conditionLetter = lock.newCondition(); // 字母条件队列
        
        new Thread(() -> {
            // method
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            lock.lock();
            try {
                // ... method body
                for (int i = 0; i < 26; i++) {
                    System.out.println((char) (a + i));
                    conditionNumber.signal();
                    conditionLetter.await();
                }
                conditionNumber.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "字母").start();

        new Thread(() -> {
            // method
            countDownLatch.countDown();

            lock.lock();
            try {
                // ... method body
                for (int i = 0; i < 26; i++) {
                    System.out.println(i + 1);
                    conditionLetter.signal();
                    conditionNumber.await();
                }
                conditionLetter.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "数字").start();
    }
}

方法四:TransferQueue阻塞队列

code

public class OutputByTurns4 {
    public static void main(String[] args) {
        int a = (int) 'a';
        // 谁transfer进去一个元素谁阻塞,需要别的线程拿走才能被唤醒
        // take的时候为空则自己入队等待,不为空则不阻塞
        // 若入队时发现有一个元素为null的节点,直接就将元素填充到该节点,并唤醒该节点等待的线程
        // ⚠️ 谁握着元素谁阻塞
        TransferQueue<String> queue = new LinkedTransferQueue<>();

        new Thread(() -> {
            // method
            for (int i = 0; i < 26; i++) {
                try {
                    System.out.println(queue.take());
                    queue.transfer(String.valueOf((char)(a+i)));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "字母").start();

        new Thread(() -> {
            // method
            for (int i = 0; i < 26; i++) {
                try {

                    queue.transfer(String.valueOf(i+1));
                    System.out.println(queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "数字").start();
    }
}