《重学Java高并发》:你是否懂Java高并发,一测便知

《重学Java高并发》专栏特色:结合10余年的工作经验,在实践中提炼总结高并发经验,将理论落到实处,不仅助力面试,更是真正提高技能。

java多线程是每一个java程序员必备的技能,但多线程对于初学者来说学起来比较困难,结合笔者多年的学习经验,能手写一个简单的生产者消费者模型,是深入了解多线程编程的前提条件。

在java中,线程与线程之间基本存在两种关系:

  • 协作 一个线程的驱动需要与另外一个线程合作,共同合作完成一件事情
  • 竞争 多个线程互斥,即多个线程只能串行执行。

本文想来和大家说说多个线程的协作,这个非常有助于理解notify、wait方法。

1、消费者/生产者场景

一个非常经典的场景:面包厂生产面包。

在一个面包厂,面包的仓库容积有限,生产工人可以继续生产面包的条件是仓库还有足够的空间,生产的面包是需要派送工人卖给顾客,派送工人要能派送面包的条件是仓库中有剩余的面包。

大概的场景到交付如下图所示: 在这里插入图片描述

2、代码实现

有了场景,接下来我们使用java写一个简易的生产者、消费者。

本示例中涉及到类主要如下图所示: 在这里插入图片描述 其类的职责说明如下:

  • Bakery 面包厂仓库,主要用来存放面包。
  • BreadWork 面包生产工人
  • BreadConsume 面包消费工人
  • Bread 面包

接下来将和大家一一展示代码,同时在介绍代码时将重点阐述线程合作时的一些重点知识,并将提出一个更高难度的思考题供大家挑战。

2.1 Bakery核心实现

Bakery是整个生产者、消费者模型的核心实现类,与多线程编程相关的核心要点也体现在该方法中,其整体代码如下: 在这里插入图片描述 其核心要点解释如下:

  • Object bakeryLock 锁对象,主要是用来保护List< Bread> 数据结构,众所周知,ArrayList是多线程不安全的,也就是说多个线程对其进行访问,必须加锁,这里之所以单独创建一个对象,主要是想突出锁概念,在代码中,其实可以用 synchronized(breads) 来代替。
  • put方法 该方法是被面包生产者调用,向面包厂中添加面包,但是面包厂的容量是有限的,即生产者不能一直往里面添加,即当达到最大容量后,需要阻止生产者继续往里面添加,故这里涉及到条件等待与wait方法,细细说明如下:
    • 访问breads数据结构之前,先使用synchronized进行保护,即加锁。
    • 如果仓库已满,需要调用锁对象的wait方法,调用锁对象的线程,也就是生产者线程会被阻塞,需要等待其他线程的唤醒,唤醒后才能继续执行后续代码。
    • 如果仓库还有空间,则向仓库中添加一个面包,此时另外一个隐含的条件将满足:仓库中已经有面包了,而消费者可能会因为仓库中没有面包而阻塞,故这里需要调用锁对象的notify或nofifyAll方法,唤醒等待的消费者。
  • get方法 该方法主要是被面包消费者调用,从面包中获取面包,但要能获取面包也是有条件的:仓库中存在面包,否则需要阻塞等待消费者创建面包,故这里的要点如下:
    • 如果仓库中没有面包,调用锁对象的wait方法,则消费者线程将进入阻塞状态,其具体实现是消费者线程对象会放在锁对象的条件等待队列,将等待其他线程调用锁的notify或notifyAll。
    • 如果有面包,则从中消费一个面包,此时另外一个隐含的条件将满足:仓库中已经有新的空间存放新的面包,故此时应该调用锁对象的notify或notifyAll,唤醒生产者。

请大家细品:notify,wait方法是调用的锁对象,并不是生产者或消费者线程。 欢迎大家私信我,交流心得体会。

2.2 生产者/消费者代码实现

在该示例中生产者、消费者创建面包,并尝试存储在面包厂中,其代码示例如下: 在这里插入图片描述 消费者、生产者代码比较简单,就不做过多说明。

温馨提示:如果需要整套代码,可以私信我,回复TCODE即可获得。

2.3 运行效果与进阶

该示例的运行效果如下图所示: 在这里插入图片描述 上面的示例其实只是一个入门,重点是了解锁对象,线程之间如何通过notify、wait方法进行协同“作战”,有了上面的示例,我想将难度系数再次提高:

如果做到生产者、消费者交替运行,即生产者生产面包1号,需要等待消费者消费完面包1号后,生产者才能继续生产2号面包。

各位读者朋友们,如果想要深入彻底理解多线程编程,线程协作是必不可少的,希望大家能对上述题进行响应,欢迎大家加我微信,一起拉探讨实现。

版权信息:本文由中间件兴趣圈创作

禁止非授权转载,违着依法追究相关法律责任

如需转载,请联系 codingw@126.com