面试题 java wait 和 notify 用法

4 阅读2分钟

面试题

有三个线程ABC分别向一个数组中写入a,l,i,要求最终的写入结果形如alialiali...写入次数由A线程决定

解法1:使用Object的 wait 和 notify

package org.example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Sync1 {
    List<Character> result = new ArrayList<>(); // 共享数组
    volatile int total = 5; // 总写入次数(由A线程决定)
    volatile int current = 0; // 当前写入次数
    volatile String currentThread = "A"; // 当前执行线程标识
    final Object lock = new Object(); // 同步锁

    Runnable a = () -> {
        while (true) {
            synchronized (lock) {
                // 等待当前执行线程为A且未达到总次数
                while (!currentThread.equals("A") && current < total) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (current >= total) break; // 达到总次数退出

                result.add('a');
                current++; // 仅A线程递增计数器
                currentThread = "B"; // 切换到B线程
                lock.notifyAll();  // 通知其他线程
            }
        }
    };
    Runnable b = () -> {
        while (true) {
            synchronized (lock) {
                // 等待当前执行线程为B且未达到总次数
                while (!currentThread.equals("B") && current <= total) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                result.add('l');
                currentThread = "C"; // 切换到C线程
                lock.notifyAll();

                if (current >= total) break;
            }
        }
    };
    Runnable c = () -> {
        while (true) {
            synchronized (lock) {
                // 等待当前执行线程为C且未达到总次数
                while (!currentThread.equals("C") && current <= total) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                result.add('i');
                currentThread = "A"; // 切换回A线程
                lock.notifyAll();
                if (current >= total) break;
            }
        }
    };

    private void test() throws InterruptedException {
        Thread threada = new Thread(a);
        Thread threadb = new Thread(b);
        Thread threadc = new Thread(c);
        threada.start();
        threadb.start();
        threadc.start();

        threada.join();
        threadb.join();
        threadc.join();

        System.out.println(Arrays.toString(result.toArray()));
    }


    public static void main(String[] args) {
        try {
            new Sync3().test();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

解法2:使用 Lock.Condition 的 await 和 signal

package org.example;

import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
import java.util.*;

public class Sync2 {
    private static final int TOTAL = 5; // 由A线程决定的总写入次数
    private static final List<Character> result = Collections.synchronizedList(new ArrayList<>());
    private static final AtomicInteger current = new AtomicInteger(0);
    private static final AtomicBoolean termination = new AtomicBoolean(false);
    private static final Lock lock = new ReentrantLock();
    private static final Condition aCond = lock.newCondition();
    private static final Condition bCond = lock.newCondition();
    private static final Condition cCond = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(SyncWriter::threadA);
        Thread b = new Thread(SyncWriter::threadB);
        Thread c = new Thread(SyncWriter::threadC);
        a.start();
        b.start();
        c.start();

        Thread.sleep(10);
        // 初始触发A线程
        lock.lock();
        System.out.println("main 获取锁");
        try {
            aCond.signal();
        } finally {
            lock.unlock();
            System.out.println("main 释放锁");
        }

        a.join();
        b.join();
        c.join();
        System.out.println(result);
    }

    private static void threadA() {
        while (!termination.get()) {
            lock.lock();
            System.out.println("threadA 获取锁");
            try {
                aCond.await();
                System.out.println("threadA 突破await");
                if (termination.get()) break;
                result.add('a');
                int next = current.incrementAndGet();
                if (next >= TOTAL) termination.set(true);
                bCond.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
                System.out.println("threadA 释放锁");
            }
        }

        System.out.println("threadA over");
    }

    private static void threadB() {
        while (!termination.get()) {
            lock.lock();
            System.out.println("threadB 获取锁");
            try {
                bCond.await();
                System.out.println("threadB 突破await");
                result.add('l');
                cCond.signal();
                if (termination.get()) break;
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
                System.out.println("threadB 释放锁");
            }
        }

        System.out.println("threadB over");
    }

    private static void threadC() {
        while (!termination.get()) {
            lock.lock();
            System.out.println("threadC 获取锁");
            try {
                cCond.await();
                System.out.println("threadC 突破await");
                result.add('i');
                aCond.signal();
                if (termination.get()) break;
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
                System.out.println("threadC 释放锁");
            }
        }

        System.out.println("threadC over");
    }
}