恋爱中的Java多线程:从单身到共舞的浪漫指南(一)

引言:孤独的线程,寂寞的码农

  • 开篇小剧场

    ​ 深夜,孤独的程序猿凯叔接到新任务:优化程序性能,探索多线程。这一任务成了他跳出孤独、寻求生活并行美好的契机。从简单的Thread类到复杂的线程池管理,每一步都充满了挑战,也伴随着惊喜。就像是初次尝试约会,既紧张又满怀期待,就像遇见了爱情,又像是一场索然无味的单相思,他遇见了死锁的难题,程序像陷入了一场尴尬的沉默,彼此等待着对方先放手。那一刻,他恍然大悟,这不正是单恋的人之间沟通时常遇到的问题吗?需要适时放手,给予对方空间,才能顺畅前行。多程的世界和现实世界竟是如此相似,不仅更教会了凯叔如何在人际关系中寻找平衡,如何在繁忙中不失温柔,如何在并行中保持和谐,凯叔笑了,这不就是爱情么。

  • 技术邂逅爱情

    ​ 多线程就是一个完美的时间管理大师,他将CPU的利用率发挥到了极致,可以说多核CPU的诞生,使得我们可以利用多线程去做一些我们只能假设的想法。多线程可以被看作是一位时间管理大师,它能够同时管理多个任务(或者说是“备胎”),每个线程都像是海王鱼塘中的一员,各自忙碌却可能对全局状况不完全知晓。它们共享资源,协同工作,确保程序能够高效利用CPU时间,完成多项任务而无需等待。不过,与“海王”不同的是,多线程的目标是高效与和谐,避免冲突和误解(比如死锁和竞态条件),确保所有线程能够顺畅执行,达成程序的最终目标。这样看来,多线程更像是一位细心的协调者,而非玩转情感的游戏高手。

第一章:《初遇:单线程的孤独星球》

  • 单线程的独白

    ​ 单线程程序,恰似苦逼程序猿凯叔的孤独加班夜。凯叔就是整个项目的唯一执行者,从接收需求、编码实现到测试调试,所有环节都得亲力亲为,无人分担。就像一台计算机上的单线程程序,它在同一时间内只能专注于处理一项任务,从头至尾,按部就班,不能分身。

    ​ 想象一下,凯叔坐在空荡荡的办公室,四周只有敲击键盘的声音陪伴。他先完成数据库查询(第一幕),接着处理数据逻辑(第二幕),最后输出结果(第三幕),每个步骤都要等到前一步彻底完成后才能开始。这样的工作模式稳定而简单,却极其低效,尤其是当任务繁多或某些操作耗时较长时,凯叔只能默默守候,夜越来越深,咖啡一杯接一杯,心中不禁憧憬着能有同事加入,大家分工合作,共同加速项目的推进。

    在这里插入图片描述

  • 寂寞的等待:就像等待恋人的回信时,你是选择一直盯着手机还是继续生活?

    ​ 在Java中,多线程的阻塞与非阻塞是描述线程在执行过程中遇到特定情况时的行为特征,主要涉及线程对于资源访问、任务等待及执行控制等方面。

    ​ 首先先讲一下基本概念:阻塞、非阻塞。这两个对于多线程来讲永远绕不开的话题,也是老生常谈的话题,面试也是必问的话题,我们不能逃避,也无法回避这个问题。这两个概念在编程中就如同等待恋人回信时的不同态度,既生动又贴切。

    阻塞(Blocking)

    ​ 当一个线程执行到某个操作时,如果该操作因为某些条件未满足而无法继续执行,导致线程进入等待状态,直到条件满足后才恢复执行,这个过程称为阻塞。阻塞期间,线程会释放CPU控制权,不消耗CPU时间,等待操作系统或外部事件的通知。

    ​ 就好比你发送了一条满载深情的信息给心仪的TA后,便开始了无尽的等待。你放下一切,眼睛紧紧盯着手机屏幕,连呼吸都似乎在暂停,生怕错过任何一个可能的震动或提示音。在这期间,你几乎不做其他任何事,整个世界仿佛都静止了,这就是阻塞状态——程序在等待某个操作(比如I/O操作)完成前,会暂停执行,不进行其他任务。

    常见场景

    • 等待资源: 当线程试图获取一个已经被其他线程持有的锁(例如,通过synchronized关键字或Lock接口)时,它会被阻塞,直到持有锁的线程释放锁。
    • I/O操作: 执行网络读写、文件读写等I/O操作时,如果没有设置为非阻塞模式,线程会阻塞直到操作完成。
    • 等待条件: 使用Object.wait()Condition.await()等方法等待特定条件满足时,线程也会进入阻塞状态
    非阻塞(Non-blocking)

    ​ 非阻塞操作允许线程在调用无法立即完成的情况下,不挂起线程,而是立即返回,允许线程继续执行其他任务。这意味着线程不会因为一个操作未完成而完全停止工作,提高了CPU的利用率和程序的响应性。

    ​ 非阻塞像是一种更为洒脱的态度。信息发送出去后,你选择继续你的生活:看书、健身、或是和朋友相聚,手机放在一旁,虽然心里有所期待,但并不让它成为生活的全部。当消息来临时,自然会知道,但这不妨碍你享受当前的每一刻。在编程中,非阻塞意味着程序发起请求后,并不等待结果,而是继续执行后续代码,它可能通过轮询、回调函数或事件通知等方式,在未来某个时刻得知操作的结果,从而实现了高效率的并发处理。

    实现机制

    • 轮询: 不断检查操作是否完成,这种方式可能会消耗较多CPU资源。
    • 回调函数: 提供一个函数,当操作完成时由系统调用,避免了主动等待。
    • Future/Promise模式: 异步获取结果,线程可以继续执行,稍后通过Future对象查询或接收完成通知。
    Java中的应用
    • NIO (New Input/Output): 提供了非阻塞的I/O操作,通过Selector选择器可以监控多个通道的事件,无需为每个连接分配单独的线程。
    • 并发工具类: ConcurrentHashMap, CountDownLatch, Semaphore等提供了非阻塞或有限阻塞的机制来协调线程间的操作。
    PS:这里提到了NIO,其实关于NIO,IO,BIO也是一个面试中经常问到的问题。这里简单介绍一下。

    同步阻塞I/O(BIO)

    同步阻塞I/O,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制来改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务端资源要求比较高,并发局限于应用中,在jdk1.4以前是唯一的io现在,但程序直观简单易理解

    同步非阻塞I/O(NIO)

    同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,jdk1,4开始支持

    异步非阻塞I/O(AIO)

    异步非阻塞I/O,服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程进行处理。AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,jdk1.7开始支持。

    IO与NIO区别

    ​ Java NIO(New IO)是从 Java 1.4 版本开始引入的一个新的 IO API,可以替代标准的 Java IO API。NIO 与原来的 IO 有同样的作用和目的,但是使用方式完全不同,NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。

    ​ Java NIO 系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。简而言之,Channel 负责传输,Buffer 负责存储

未完待续:后面《情窦初开:多线程的甜蜜相遇》,主要先讲一下同步异步是啥子个回事儿

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值