线程3:线程间通信

本文介绍了三种线程间通信方式:传统同步监视器、Condition条件变量及阻塞队列(BlockingQueue)。通过具体代码示例展示了如何在多线程环境中实现线程间的同步与通信,并详细解释了wait、notify、notifyAll等方法的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)传统线程通信

(有同步监视器,synchronozed代码块或方法均可)

wait();      阻塞,不同于死锁
notify();    随机唤醒同步监视器上的一个线程
notifyAll(); 唤醒同步监视器上的所有线程

说明:可以先唤醒其他线程,但是不会执行,当前线程wait()后才会执行
     可以先wait再notify。如先wait即被阻塞,notify不会执行
存取交互
public synchronized void draw(double drawAmount)
{
    // 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
    if (!flag)
    {
        wait();
    }
    else
    {
        // 执行取钱
        System.out.println(Thread.currentThread().getName()
            + " 取钱:" +  drawAmount);
        balance -= drawAmount;
        System.out.println("账户余额为:" + balance);

        // 将标识账户是否已有存款的旗标设为false。       
        flag = false;    //先锁定,即wait()

        // 唤醒其他线程
        notifyAll();     //再唤醒
    }

}

public synchronized void deposit(double depositAmount)
    {
        // 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
        if (flag)             //①
        {
            wait();
        }
        else
        {
            // 执行存款
            System.out.println(Thread.currentThread().getName()
                + " 存款:" +  depositAmount);
            balance += depositAmount;
            System.out.println("账户余额为:" + balance);

            // 将表示账户是否已有存款的旗标设为true
            flag = true;

            // 唤醒其他线程
            notifyAll();
        }
    }

(二)Condition控制线程通信

用Look来同步线程时

private final Lock lock = new ReentrantLock();// 显式定义Lock对象
// 获得指定Lock对象对应的Condition。newCondition()方法
private final Condition cond  = lock.newCondition();


public void draw(double drawAmount)
    {
        // 加锁
        lock.lock();
        try
        {
            // 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
            if (!flag)
            {
                cond.await();
            }
            else
            {
                // 执行取钱
                System.out.println(Thread.currentThread().getName()
                    + " 取钱:" +  drawAmount);
                balance -= drawAmount;
                System.out.println("账户余额为:" + balance);
                // 将标识账户是否已有存款的旗标设为false。
                flag = false;
                // 唤醒其他线程
                cond.signalAll();
            }
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
        // 使用finally块来释放锁
        finally
        {
            lock.unlock();
        }
    }

(三)阻塞队列(BlockingQueue)

BlockingQueue 实现是线程安全的,主要作为线程同步的工具。实现主要用于生产者-使用者队列。
BlockingQueue定义的常用方法如下:

Tables抛出异常不同返回值阻塞线程指定超时时长
队尾插入add(e)offer(e)put(e)offer(e, time, unit)
头部移除remove()poll()take()poll(time, unit)
检查element()peek()


BlockingQueue也是java.util.concurrent下的主要用来控制线程同步的工具。

BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类

  1. ArrayBlockingQueue:一个由数组支持的有界阻塞队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。

  2. LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。

  3. PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。

  4. SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

    LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到puttake方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。

定义一个大小为2的BlockingQueue,当试图放第三个元素时会阻塞线程。

BlockingQueue<String> bq = new ArrayBlockingQueue<String>(2);
bq.put("java");
bq.put("python");
bq.put("js");//阻塞线程

//为空时take()也会阻塞

生产者消费者模式:

import java.util.concurrent.*;

class Producer extends Thread
{
    private BlockingQueue<String> bq;
    public Producer(BlockingQueue<String> bq)
    {
        this.bq = bq;
    }
    public void run()
    {
        String[] strArr = new String[]
        {
            "Java",
            "Struts",
            "Spring"
        };
        for (int i = 0 ; i < 999999999 ; i++ )
        {
            System.out.println(getName() + "生产者准备生产集合元素!");
            try
            {
                Thread.sleep(200);
                // 尝试放入元素,如果队列已满,线程被阻塞
                bq.put(strArr[i % 3]);
            }
            catch (Exception ex){ex.printStackTrace();}
            System.out.println(getName() + "生产完成:" + bq);
        }
    }
}

class Consumer extends Thread
{
    private BlockingQueue<String> bq;
    public Consumer(BlockingQueue<String> bq)
    {
        this.bq = bq;
    }
    public void run()
    {
        while(true)
        {
            System.out.println(getName() + "消费者准备消费集合元素!");
            try
            {
                Thread.sleep(200);
                // 尝试取出元素,如果队列已空,线程被阻塞
                bq.take();
            }
            catch (Exception ex){ex.printStackTrace();}
            System.out.println(getName() + "消费完成:" + bq);
        }
    }
}

public class BlockingQueueTest2
{
    public static void main(String[] args)
    {
        // 创建一个容量为1的BlockingQueue
        BlockingQueue<String> bq = new ArrayBlockingQueue<>(1);
        // 启动3条生产者线程
        new Producer(bq).start();
        new Producer(bq).start();
        new Producer(bq).start();
        // 启动一条消费者线程
        new Consumer(bq).start();
    }
}

由于容量为1,虽然有3个生产线程1个消费线程,每次只有1个生产者生产,然后消费。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值