Java 多线程交替打印

题目

使用三个线程 T1、T2、T3,如何让他们按顺序交替打印 10 次 A B C。

方案一:synchronized

public class SynchronizedLockPrinter {
    static class Printer {
        private final Object lock = new Object();
        private int count = 0;
        public void print(int n, int target, char content) {
            for (int i = 0; i < n; ) {
                synchronized (lock) {
                    while (count % 3 != target) {
                        try {
                            lock.wait();
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                    }
                    System.out.print(content);
                    count++;
                    i++;
                    lock.notifyAll();
                }
            }
        }

        public void print() {
            new Thread(()-> {print(10, 0, 'A');}).start();
            new Thread(()-> {print(10, 1, 'B');}).start();
            new Thread(()-> {print(10, 2, 'C');}).start();
        }
    }

    public static void main(String [] args) {
        new Printer().print();
    }
}

注意,这里需要通过 volatile 关键自

方法二:ReentrantLock

public class SignalLockPrinter {
    static class Printer {
        private final Lock lock = new ReentrantLock();
        private volatile int count = 0;

        public void print(int n, int target, char content) {
            for (int i = 0; i < n; ) {
                lock.lock();
                try {
                    while (count % 3 == target) {
                        System.out.print(content);
                        count++;
                        i++;
                    }
                } catch (Exception e) {
                    System.out.println(e);
                } finally {
                    lock.unlock();
                }
            }
        }

        public void print() {
            new Thread(()-> {print(10, 0, 'A');}).start();
            new Thread(()-> {print(10, 1, 'B');}).start();
            new Thread(()-> {print(10, 2, 'C');}).start();
        }
    }

    public static void main(String [] args) {
        new Printer().print();
    }
}

方法三:ReentrantLock + Condition(非公平锁)

public class UnfairLockConditionPrinter {
    static class ConditionPrinter {
        private final Lock lock = new ReentrantLock(); // 非公平锁
        private volatile int count = 0;
        private final int threadNumber = 10;
        private final Condition condition1 = lock.newCondition();
        private final Condition condition2 = lock.newCondition();
        private final Condition condition3 = lock.newCondition();

        public void print(int target, String content, Condition current, Condition next) {
            for (int i = 0; i < threadNumber; i++) {
                lock.lock();
                try {
                    // 执行临界区代码前判断:防止锁被不满足条件的线程抢占
                    while (count % 3 != target) {
                        current.await(); // 条件等待并释放锁
                    }
                    System.out.print(content);
                    count++; // 注意:这不是一个原子操作
                    next.signal(); // 唤醒一个等待该条件的线程
                } catch (Exception e) {
                    System.out.println(e);
                } finally {
                    lock.unlock();
                }
            }
        }

        public void print() {
            new Thread(() -> {print(0, "A", condition1, condition2);}).start();
            new Thread(() -> {print(1, "B", condition2, condition3);}).start();
            new Thread(() -> {print(2, "C", condition3, condition1);}).start();
        }
    }

    public static void main(String[] args) {
        new ConditionPrinter().print();

    }
}

注意,

  • unlock() 并不会阻塞当前线程,所以,当 A 线程在释放锁后,线程状态并没有改变,所以 A 线程还会去尝试获取一次锁,如果获取锁失败,就会进入阻塞状态;如果获取成功,就会进入条件等待状态。

  • count++ 不是一个原子操作,但是由于同时只有一个线程在执行,所以结果是正确的。

    实际使用的时候,可以替换为 LongAdderAtomicInteger 等原子类。

优化:改成公平锁,可以减少锁的竞争程度。

方法四:ReentrantLock + Condition(公平锁)

public class FairLockConditionPrinter {
    static class ConditionPrinterEnhance {
        private final Lock lock = new ReentrantLock(true);
        private final Condition condition = lock.newCondition();
        private volatile int count = 0;
        private final int threadNumber = 10;
        public void print(int target, char content) {
            lock.lock();
            try {
                for (int i = 0; i < threadNumber; i++) {
                    while (count % 3 != target) {
                        condition.await();
                    }
                    System.out.println(Thread.currentThread().getName() + ": " + content + " " + count);
                    count++;
                    condition.signal();
                }
            } catch (Exception e) {
                System.out.println(e);
            } finally {
                lock.unlock();
            }
        }

        public void print() {
            char content = 'A';
            for (int i = 0; i < 3; i++) {
                final int k = i;
                new Thread(() -> print( k, (char) (content + k))).start();
            }
        }
    }

    public static void main(String[] args) {
        new ConditionPrinterEnhance().print();
    }
}

或者

public class Test {
    private static Lock lock = new ReentrantLock();
    private static Condition c1 = lock.newCondition();
    private static Condition c2 = lock.newCondition();
    private static Condition c3 = lock.newCondition();

    private void printABC(Condition currentThread, Condition nextThread) {
        for (int i = 0; i < 10; i++) {
            lock.lock();
            try {
                System.out.print(Thread.currentThread().getName());
                nextThread.signal();    //唤醒下一个线程,而不是唤醒所有线程
                currentThread.await(); // 可以在最后一个线程执行的时候,跳过不等待,避免最后一个线程执行完后挂起
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Test print = new Test();
        new Thread(() -> {
            print.printABC(c1, c2);
        }, "A").start();
        new Thread(() -> {
            print.printABC(c2, c3);
        }, "B").start();
        new Thread(() -> {
            print.printABC(c3, c1);
        }, "C").start();
    }
}

方法五:Semaphore

public class SemaphorePrinter {

    static class Printer {
        private final int n = 10;
        private final Semaphore s1 = new Semaphore(1);
        private final Semaphore s2 = new Semaphore(0);
        private final Semaphore s3 = new Semaphore(0);
        private void print(char content, Semaphore current, Semaphore next) {
            for (int i = 0; i < n; i++) {
                try {
                    current.acquire();
                    System.out.print(content);
                    next.release();
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        }

        public void print() {
            new Thread(() -> {print('A', s1, s2);}).start();
            new Thread(() -> {print('B', s2, s3);}).start();
            new Thread(() -> {print('C', s3, s1);}).start();
        }
    }

    public static void main(String [] args) {
        new Printer().print();
    }
}
posted @ 2024-01-24 23:03  LARRY1024  阅读(801)  评论(0编辑  收藏  举报