Skip to content

线程交替打印

问题:如何让两个线程交替打印1-100的数

输出例子:

Thread1 —— 1

Thread2 —— 2

Thread1 —— 3

Thread2 —— 4

方法一:使用wait()notify()机制

在Java中,可以利用对象的wait()notify()方法来实现线程间的协调。通过在同步代码块中使用这两个方法,可以让线程在适当的时机等待或唤醒,从而实现交替打印的效果。

实例代码:

java
public class PrintNum implements Runnable {
    private int num = 1;

    @Override
    public void run() {
        synchronized (this) {
            while (num <= 100) {
                notify();
                System.out.println(Thread.currentThread().getName() + ": " + num);
                num++;
                try {
                    if (num <= 100) {
                        wait();
                    } else {
                        notify();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        PrintNum printNum = new PrintNum();
        Thread t1 = new Thread(printNum, "Thread1");
        Thread t2 = new Thread(printNum, "Thread2");
        t1.start();
        t2.start();
    }
}

方法二:使用LockCondition

Java的Lock接口及其实现类ReentrantLock提供了更灵活的线程同步控制。配合Condition接口,可以实现类似wait()notify()的功能,但控制更加精细。

实例代码:

java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintNum2 {
    private int num = 1;
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public static void main(String[] args) {
        PrintNum2 ap = new PrintNum2();
        new Thread(ap.new PrintTask(), "Thread1").start();
        new Thread(ap.new PrintTask(), "Thread2").start();
    }

    private class PrintTask implements Runnable {
        @Override
        public void run() {
            while (num <= 100) {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + ": " + num);
                    num++;
                    condition.signal();
                    if (num <= 100) {
                        condition.await();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

区分

在Java中,wait()notify()方法与ReentrantLockCondition机制都用于线程间的协调与通信,但它们在使用方式和功能特性上存在一些区别。

1. 所属类别与使用方式

  • wait()notify()方法:这两个方法是Object类的成员,必须在同步块或同步方法中使用,即需要先获得对象的监视器锁(通过synchronized关键字)后才能调用。
  • ReentrantLockCondition机制ReentrantLockLock接口的实现类,提供了显式的锁操作。Condition接口与ReentrantLock配合使用,用于线程间的协调。线程在调用Conditionawait()signal()等方法前,需要先获取相应的ReentrantLock锁。

2. 灵活性与功能特性

  • wait()notify()方法:使用wait()方法时,线程会释放当前持有的对象监视器锁,进入等待状态,直到被其他线程唤醒。notify()方法用于唤醒在该对象上等待的某个线程,notifyAll()则会唤醒所有在该对象上等待的线程。
  • ReentrantLockCondition机制ReentrantLock提供了更灵活的锁机制,例如可响应中断、实现公平锁等。Condition接口的await()方法使线程等待,并释放ReentrantLock锁,signal()方法用于唤醒等待的线程。与wait()notify()相比,Condition可以创建多个条件对象,从而实现更精细的线程控制。

3. 可中断性

  • wait()notify()方法:当线程处于wait()状态时,如果被中断,会抛出InterruptedException异常。
  • ReentrantLockCondition机制ReentrantLock提供了可中断的锁获取方式,如lockInterruptibly()方法,使线程在等待锁的过程中能够响应中断。此外,Conditionawait()方法也支持在等待过程中被中断。

4. 性能与可控性

  • wait()notify()方法:由于是Java内置的同步机制,使用简单,但在复杂的线程协调场景中,可能显得不足。
  • ReentrantLockCondition机制:提供了更高的可控性和灵活性,适用于需要精细控制线程同步的场景,但相应地,代码复杂度也会增加。

问题:如果改成N个线程(N>2)交替打印呢

我们只需要额外添加一个变量,记录当前应该执行的线程ID即可。

实例代码:

java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreeThreadPrint {
    private static final int MAX_NUM = 100;
    private int num = 1;
    private int threadIdToRun = 1; // 当前应执行的线程ID
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public static void main(String[] args) {
        ThreeThreadPrint printer = new ThreeThreadPrint();
        Thread t1 = new Thread(printer.new Printer(1), "线程1");
        Thread t2 = new Thread(printer.new Printer(2), "线程2");
        Thread t3 = new Thread(printer.new Printer(3), "线程3");
        t1.start();
        t2.start();
        t3.start();
    }

    private class Printer implements Runnable {
        private int threadId;

        public Printer(int threadId) {
            this.threadId = threadId;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (threadId != threadIdToRun) {
                        condition.await();
                    }
                    if (num > MAX_NUM) {
                        condition.signalAll();
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + ": " + num++);
                    threadIdToRun = threadId % 3 + 1;
                    condition.signalAll();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}