【java多线程】线程间通信-利用wait和notify轮流按序打印奇数和偶数

线程之间通信,可以用wait和notify来实现。

(一)对wait的理解

wait是object的一个方法,表示是当前执行代码的线程进行等待,且会释放当前的锁,满足一定条件时被唤醒,然后重新尝试获取锁。这样说明,必须先持有锁,才能执行wait语句,所以wait一般是用在synchronized代码块中。

wait如果需要结束等待,可以通过其他线程调用该对象的notify方法,也可以在wait时设置等待超时时间,或者通过其他线程调用等待线程的interrupt方法,导致wait抛出interruptedException异常

(二)对notify的理解

notify是唤醒等待的线程,notify用来唤醒那些等待当前对象持有的锁的其他线程。如果只需要唤醒1个等待的线程,直接用notify即可。如果需要从多个等待的线程中,随机唤醒1个,则需要用notifyAll,同样的notify也需要在synchronized同步代码块中使用到

(三)下面我们通过一个例子来说明wait和notify的使用,并讲述线程间是如何通信的。这个例子的代码如下,这个例子实现了用多线程轮流按序来打印奇数和偶数。

package com.company;

public class JishuAndOuShuTest {
    public static void main(String[] args) {
        Data data = new Data();
        //线程数量
        int THREAD_COUNT = 3;
        Thread[] threadJiShuArray = new Thread[THREAD_COUNT];
        Thread[] threadOuShuArray = new Thread[THREAD_COUNT];

        //初始化奇数线程和偶数线程
        for (int i = 0; i < THREAD_COUNT; i++) {
            //奇数打印线程数组
            threadJiShuArray[i] = new Thread(() -> {
                while (data.getCount() <= 100) {
                    data.printJiShu();
                }
            }, "奇数线程" + i);

            //偶数打印线程数组
            threadOuShuArray[i] = new Thread(() -> {
                while (data.getCount() <= 100) {
                    data.printOuShu();
                }
            }, "偶数线程" + i);
        }

        //启动奇数线程和偶数线程
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadJiShuArray[i].start();
            threadOuShuArray[i].start();
        }
    }
}

class Data {

    /**
     * 需要打印的数据
     */
    private int count = 1;

    public synchronized int getCount() {
        return count;
    }

    /**
     * 打印奇数
     */
    public synchronized void printJiShu() {
        if (count % 2 == 1) {
            System.out.println(Thread.currentThread().getName() + "打印" + count);
            count++;
            this.notifyAll();
        } else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 打印偶数
     */
    public synchronized void printOuShu() {
        if (count % 2 == 0) {
            System.out.println(Thread.currentThread().getName() + "打印" + count);
            count++;
            this.notifyAll();
        } else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如下:

奇数线程0打印1
偶数线程0打印2
奇数线程0打印3
偶数线程0打印4
奇数线程0打印5
偶数线程0打印6
奇数线程1打印7
偶数线程0打印8
奇数线程0打印9
偶数线程1打印10
奇数线程1打印11
偶数线程0打印12
奇数线程2打印13
偶数线程1打印14
奇数线程1打印15
偶数线程2打印16
奇数线程2打印17
偶数线程1打印18
奇数线程1打印19
偶数线程2打印20
奇数线程2打印21
偶数线程1打印22
奇数线程1打印23
偶数线程2打印24
奇数线程2打印25
偶数线程1打印26
奇数线程1打印27
偶数线程2打印28
奇数线程2打印29
偶数线程1打印30
奇数线程1打印31
偶数线程2打印32
奇数线程2打印33
偶数线程1打印34
奇数线程1打印35
偶数线程2打印36
奇数线程2打印37
偶数线程1打印38
奇数线程1打印39
偶数线程2打印40
奇数线程2打印41
偶数线程1打印42
奇数线程1打印43
偶数线程2打印44
奇数线程2打印45
偶数线程1打印46
奇数线程1打印47
偶数线程2打印48
奇数线程2打印49
偶数线程1打印50
奇数线程1打印51
偶数线程2打印52
奇数线程2打印53
偶数线程1打印54
奇数线程0打印55
偶数线程2打印56
奇数线程1打印57
偶数线程1打印58
奇数线程0打印59
偶数线程2打印60
奇数线程1打印61
偶数线程1打印62
奇数线程0打印63
偶数线程2打印64
奇数线程1打印65
偶数线程1打印66
奇数线程0打印67
偶数线程2打印68
奇数线程1打印69
偶数线程1打印70
奇数线程0打印71
偶数线程2打印72
奇数线程1打印73
偶数线程1打印74
奇数线程0打印75
偶数线程2打印76
奇数线程1打印77
偶数线程1打印78
奇数线程0打印79
偶数线程2打印80
奇数线程1打印81
偶数线程1打印82
奇数线程0打印83
偶数线程2打印84
奇数线程1打印85
偶数线程1打印86
奇数线程0打印87
偶数线程2打印88
奇数线程1打印89
偶数线程1打印90
奇数线程0打印91
偶数线程2打印92
奇数线程1打印93
偶数线程1打印94
奇数线程0打印95
偶数线程2打印96
奇数线程1打印97
偶数线程1打印98
奇数线程0打印99
偶数线程2打印100
Disconnected from the target VM, address: '127.0.0.1:61821', transport: 'socket'

Process finished with exit code 0

在上面代码中,我们能看到wait和notifyAll是用在synchronized同步方法中,我们设置了3个奇数打印线程和3个偶数打印线程,来轮流交替打印1-100之间的奇数和偶数。

代码运行流程如下:

首先会判断当前数是否小于100,如果小于100,则奇数线程和偶数线程都会开始运行。当奇数线程运行时,在printJiShu同步方法中,会判断当前数据是否为奇数,如果是奇数,则会打印出来,并且再自增1,因为数据就变成了偶数,然后当前线程就通过notify唤醒其他线程,然后此时数据还是小于100,奇数线程会再次执行while语句中的代码块,因为当前数据是偶数了,会走else分支,然后当前线程通过wait方法,进入等待状态。而偶数线程的执行过程也类似,当判断当前数字小于100,偶数线程会开始运行,然后判断当前数字是否为偶数,如果是偶数,则会打印出来,并执行自增1的操作,然后又会唤醒其他出于wait的线程。此时可能唤醒奇数线程,如果奇数线程被唤醒,因为次数数字又变成了奇数,然后又会执行奇数打印,然后再自增1,之后再进行唤醒其他线程,如此循环。直至打印的数字变成100,然后跳出while循环,从而线程执行结束。

<think>好的,我现在需要帮助用户了解如何通过编程实现两个线程交替打印。用户已经提供了一些参考资料,包括使用JavaPython的例子,以及涉及synchronized、waitnotify等方法。我需要先整理这些信息,然后给出清晰的步骤示例代码。 首先,用户的问题是关于两个线程交替打印的实现方法,可能是指交替打印偶数,或者交替打印不同的字符。根据提供的引用,常见的做法是使用线程间的同步机制,比如锁条件变量,或者使用wait/notify来控制线程的执行顺序。 我需要考虑不同编程语言的实现,用户提供的引用中有JavaPython的例子。但用户的问题没有指定语言,可能需要给出两种语言的示例,或者选择其中一种。不过用户之前的问题可能偏向Python,因为最后的示例代码是Python的快速排序,但引用的资料中有JavaPython的例子。需要确认用户的需求,但根据问题描述,用户可能希望得到通用的方法,或者具体语言的示例。 另外,用户提到“交替打印”,可能是指两个线程轮流执行,比如一个打印奇数,一个打印偶数,或者交替打印AB。需要明确场景,但根据引用中的例子,奇偶数交替打印比较常见。 接下来,我需要整理实现步骤。通常,实现线程交替打印需要以下步骤: 1. 创建共享对象或锁,用于线程同步。 2. 定义两个线程,每个线程负责打印特定的内容。 3. 使用同步机制确保每次只有一个线程执行打印操作。 4. 通过waitnotify(或类似机制)控制线程的交替执行。 在Java中,可以使用synchronized关键字配合waitnotify,或者使用ReentrantLockCondition。在Python中,由于GIL的存在,多线程可能不如多进程高效,但可以使用threading模块中的LockCondition来实现。 需要检查引用中的示例,例如引用[1][4]提到Java中使用synchronizedwait/notify,而引用[2][5]提到Python中的多线程交替打印。引用[3]提到使用Condition对象。 现在,我需要为两种语言分别给出示例代码,并解释其步骤。但用户可能希望得到一个具体的例子,比如交替打印偶数,或者交替打印ABAB等。 根据用户的问题,他们可能希望看到两种方法的对比,比如使用synchronizedwait/notify的方法,或者使用LockCondition的方法。需要参考提供的引用,例如引用[4]中的实现方式,以及引用[3]中的Condition用法。 在Java中,基本的实现步骤是: - 共享一个锁对象。 - 线程1获取锁后,检查条件是否满足(如是否是偶数),满足则打印并递增,然后唤醒另一个线程,否则等待。 - 线程2类似,检查是否是奇数打印后唤醒另一个线程。 在Python中,由于没有内置的wait/notify机制,可以使用threading.Condition来实现类似的功能。或者使用Event对象。 需要注意的是,在Python中,多线程由于GIL的关系,可能不会真正并行执行,但交替打印可以通过同步机制实现。 另外,需要考虑线程启动的顺序,可能需要一个标志变量来确定当前应该由哪个线程执行。 现在,需要编写示例代码。例如,在Java中,使用synchronized的代码可能如下: class Shared { int count = 1; boolean isOdd = true; } 线程1(奇数线程): synchronized (shared) { while (shared.count <= max) { if (shared.isOdd) { print; shared.count++; shared.isOdd = false; shared.notify(); } else { shared.wait(); } } } 线程2(偶数线程)类似,检查isOdd是否为false。 在Python中,可以使用threading.Condition: import threading class Shared: def __init__(self): self.count = 1 self.cond = threading.Condition() self.is_odd = True def print_odd(shared, max): with shared.cond: while shared.count <= max: if shared.is_odd: print(shared.count) shared.count +=1 shared.is_odd = False shared.cond.notify() else: shared.cond.wait() def print_even(shared, max): with shared.cond: while shared.count <= max: if not shared.is_odd: print(shared.count) shared.count +=1 shared.is_odd = True shared.cond.notify() else: shared.cond.wait() 然后创建两个线程,启动它们。 不过需要确保线程正确退出,否则可能出现无法结束的情况,如引用[5]中提到的问题,可能需要检查条件是否满足,并在适当的时候退出循环。 还需要考虑最大次数,比如打印到10为止。 另外,在Python中,由于线程可能因为wait而阻塞,需要确保在count超过max时,线程能够退出循环。 例如,在print_odd函数中: while shared.count <= max: if ...: ... else: shared.cond.wait() 但如果在等待时,count已经超过max,线程可能无法退出。因此,可能需要使用带有超时的wait,或者在notify时确保所有线程都能被唤醒。或者,在循环条件中不仅要检查count是否超过max,还要处理被唤醒后的状态。 这可能需要更复杂的条件判断。 另外,在Java中,使用两个Condition对象可能更高效,如引用[3]提到的,每个线程有自己的Condition,这样在唤醒时只唤醒另一个线程,而不是所有等待的线程,避免虚假唤醒。 综上,我需要整理出两种实现方法:使用synchronizedwait/notify,以及使用LockCondition(可能更高效)。然后给出JavaPython的示例代码,并解释每个步骤。 在回答中,需要结构清晰,分步骤说明,并确保代码正确,避免死锁或无法退出的情况。同时,需要引用提供的参考资料,例如引用[1]、[3]、[4]中的方法。</think>### 两个线程交替打印的实现方法 实现两个线程交替打印的核心在于**线程同步****条件判断**。以下是两种常见实现方式的详细步骤及示例代码: --- ### 方法一:使用`synchronized``wait/notify`(Java) 通过共享对象的锁机制,控制线程交替执行。 #### 实现步骤: 1. **定义共享对象**:存储当前打印的数值线程状态。 2. **线程1打印奇数**:检查当前数值是否为奇数,满足则打印并唤醒线程2,否则等待。 3. **线程2打印偶数**:检查当前数值是否为偶数,满足则打印并唤醒线程1,否则等待。 ```java public class AlternatePrint { private static final Object lock = new Object(); private static int count = 1; private static final int MAX = 10; public static void main(String[] args) { new Thread(() -> { synchronized (lock) { while (count <= MAX) { if (count % 2 == 1) { System.out.println("Thread 1: " + count++); lock.notify(); // 唤醒线程2 } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); new Thread(() -> { synchronized (lock) { while (count <= MAX) { if (count % 2 == 0) { System.out.println("Thread 2: " + count++); lock.notify(); // 唤醒线程1 } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); } } ``` **关键点**: - `wait()`释放锁并进入等待状态,`notify()`唤醒其他线程[^4]。 - 必须使用循环检查条件,避免虚假唤醒[^5]。 --- ### 方法二:使用`Condition``ReentrantLock`(Java) 通过更细粒度的锁控制,减少不必要的唤醒。 #### 实现步骤: 1. **定义锁条件变量**:每个线程关联一个`Condition`。 2. **交替触发条件**:奇数线程完成后触发偶数线程的条件,反之亦然。 ```java import java.util.concurrent.locks.*; public class AlternatePrintWithCondition { private static ReentrantLock lock = new ReentrantLock(); private static Condition oddCondition = lock.newCondition(); private static Condition evenCondition = lock.newCondition(); private static int count = 1; private static final int MAX = 10; public static void main(String[] args) { new Thread(() -> { lock.lock(); try { while (count <= MAX) { if (count % 2 == 1) { System.out.println("Thread 1: " + count++); evenCondition.signal(); // 通知偶数线程 } else { oddCondition.await(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }).start(); new Thread(() -> { lock.lock(); try { while (count <= MAX) { if (count % 2 == 0) { System.out.println("Thread 2: " + count++); oddCondition.signal(); // 通知奇数线程 } else { evenCondition.await(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }).start(); } } ``` **关键点**: - 每个线程通过`Condition.await()`等待,通过`Condition.signal()`精准唤醒目标线程[^3]。 - 避免全局锁竞争,提升效率。 --- ### 方法三:使用`threading.Condition`(Python) Python中通过条件变量实现类似逻辑。 ```python import threading class SharedState: def __init__(self): self.count = 1 self.cond = threading.Condition() self.is_odd = True def print_odd(shared, max_count): with shared.cond: while shared.count <= max_count: if shared.is_odd: print(f"Thread 1: {shared.count}") shared.count += 1 shared.is_odd = False shared.cond.notify() else: shared.cond.wait() def print_even(shared, max_count): with shared.cond: while shared.count <= max_count: if not shared.is_odd: print(f"Thread 2: {shared.count}") shared.count += 1 shared.is_odd = True shared.cond.notify() else: shared.cond.wait() shared = SharedState() max_num = 10 t1 = threading.Thread(target=print_odd, args=(shared, max_num)) t2 = threading.Thread(target=print_even, args=(shared, max_num)) t1.start() t2.start() t1.join() t2.join() ``` **关键点**: - `with shared.cond`自动获取释放锁。 - `notify()`唤醒等待的线程,`wait()`释放锁并阻塞[^2]。 --- ### 对比分析 | 方法 | 优点 | 缺点 | |----------------|-------------------------------|------------------------------| | `synchronized` | 代码简单,无需额外依赖 | 唤醒不精准,可能产生竞争 | | `Condition` | 精准唤醒,减少锁竞争 | 代码复杂度稍高 | | Python实现 | 语法简洁,适合轻量级任务 | 因GIL限制,不适合CPU密集型任务 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值