第5章:线程间通信(Java高并发编程详解:多线程与系统设计)

1.同步阻塞与异步非阻塞

1.1同步阻塞消息处理

  • 同步Event提交, 客户端等待时间过长 (提交Event时长+接受Event创建thread时 长+业务处理时长+返回结果时长)会陷入阻塞, 导致二次提交Event耗时过长。
  • 由于客户端提交的Event数量不多, 导致系统同时受理业务数量有限,也就是系统整体的吞吐量不高。
  • 这种一个线程处理一个Event的方式,会导致出现频繁的创建开启与销毁,从而增加系统额外开销。
  • 在业务达到峰值的时候,大量的业务处上理线程阻塞会导致频繁的CPU上下文切换,从而降低系统性能。

1.2异步飞阻塞消息处理

        客户端提交Event后会得到一个相应的工单并且立即返回, Event则会被放置在Event 队列中。服务端有若干个工作线程, 不断地从Event队列中获取任务并且进行异步处 理, 最后将处理结果保存至另外一个结果集中,如果客户端想要获得处理结果,则可凭借工单 号再次查询。

        两种方式相比较,你会发现异步非阻塞的优势非常明显,首先客户端不用等到结果 处 理结束之后才能返回,从而提高了系统的吞吐量和并发量;其次若服务端的线程数量在一 个可控的范围之内是不会导致太多的CPU上下文切换从而带来的额外开销的; 再次服务端 线程可以重复利用,这样就减少了不断创建线程带来的资源浪费。

2.单线程间通信

在1.2节中, 服务端有若干个线程会从队列中获取相应的Event进行异步处理, 那 么 这些线程又是如何从队列中获取数据的呢?换句话说就是如何知道队列里此时是否有数据 呢?比较笨的办法是不断地轮询:如果有数据则读取数据并处理,如果没有则等待若干 时 间再次轮询。还有一种比较好的方式就是通知机制:如果队列中有Event, 则通知工作的线程开始工作;没有Event,则工作线程休息并等待通知。

2.1初试wait和notify

在本节中, 我们将会学习线程之间如何进行通信, 首先实现一个EventQueue, 该Queue有如下三种状态:

    • 队列满——最多可容纳多少个Event, 好比一个系统最多同时能够受理多少业务一样;
    • 队列空一 当所有的Event都被处理并且没有新的Event被提交的时候, 此时队列将是空的状态;
    • 有Event但是没有满—— 有新的Event被提交, 但是此时没有到达队列的上限。

EventQueue队列:


import java.util.LinkedList;

public class EventQueue {

    private final int max;

    static class Event{

    }

    private final LinkedList<Event> eventQueue = new LinkedList<>();
    private final static int DEAULT_MAX_EVENT=10;

    public EventQueue() {
        this(DEAULT_MAX_EVENT);
    }

    public EventQueue(int max){
        this.max = max;
    }

    public void offer(Event event) {
        synchronized (eventQueue) {
            if(eventQueue.size() >= max) {
                try {
                    System.out.println("the queue is full");
                    eventQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("the new event is submitted");
            eventQueue.addLast(event);
            eventQueue.notify();
        }

    }

    public Event take() {
        synchronized (eventQueue) {
            if ( eventQueue.isEmpty()) {

                try {
                    System.out.println("the queue is empty");
                    eventQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


            }

            Event event = eventQueue.removeFirst();
            this.eventQueue.notify();
            System.out.println("the event " + event + " is handled");
            return event;
        }

    }

}

上述代码中,在EventQueue中定义了一个队列,offer方法会提交一个Event至队尾,如果此时队列已经满了,那么提交的线程将会被阻塞,这是调用了wait方法的结果(后 文中会重点介绍wait方法) 。同样take方法会从队头获取数据,如果队列中没有可用数据, 那么工作线程就会被阻塞,这也是调用wait方法的直接结果。此外,还可以看到一个notify方法,该方法的作用是唤醒那些曾经执行 monitor的wait方法而进入阻塞的线程。

EventClient测试代码:


import java.util.concurrent.TimeUnit;

public class EventClient {

    public static void main(String[] args) {
        final EventQueue eventQueue = new EventQueue();

        new Thread(
                ()->{
                    for(;;) {
                        eventQueue.offer(new EventQueue.Event());
                    }
                }
                ,"Producer"
        ).start();

        new Thread(
                ()->{
                    for(;;) {
                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扫地僧009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值