走进Java并发编程01
前言
本系列文章作为学习笔记,记录了博主向并发包进攻的过程。
在此,我假设本文读者已经掌握了Java的基本编码,如果是大学生,至少Java的课程要通过;如果是培训学员,至少需要学完SE部分。虽然影响不大,但是如果对Spring,或者至少Java Web有一定了解和编码经验,应该会对阅读有所帮助(我可能会举一些网站相关的例子)。
本文夹杂大量个人理解,错误不可避免,非常欢迎评论指正。
正文
第一章 :假如没有juc……
2004年9月30日18:00PM,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE1.5更名为Java SE 5.0。——via 维基百科
前置知识:JSR——Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。
JSR166完成(同样也是J2SE1.5发布)之后,Java程序员发现,除了Thread类、Runnable接口外,Java引入了大量的API,极大的丰富了Java在多线程编程的能力。
让我们从已掌握的知识开始,先忘掉java.util.concurrent,复习一下基础的概念:线程/进程/并发/并行,还有我们的老朋友Thread/Runnable/synchronized/volatile。
第一节:进程与线程。
在提到线程和进程之前,推荐一篇有趣的文章:
《我是一个CPU:这个世界慢!死!了! 》
(阅读前需要对计算机的基本组成有一定了解,至少要明白SSD、CPU的L1L2缓存是什么。)
如果将一颗2.6GHz频率,一个逻辑核心的CPU(这个性能在现在可以说是很低了,主流的i7-8700K有6个物理核心,通过Intel的超线程技术让系统认为有12个逻辑核心,最高睿频4.7GHz)执行每个指令的时间放大到一秒,那么从内存中读取1MB 需要7.5天,从SSD上的随机读取需要用4.5天,从SSD读取1MB需要一个月,而HDD的磁盘寻址需要10个月,连续读取1MB需要20个月!
我们设想一个场景。你打开一台一个逻辑核心的电脑,登录QQ,同时打开IDEA开始编写代码,在你眼里QQ和IDEA是同时运行着的程序。但是在CPU的眼里,QQ和IDEA需要CPU来执行的操作其实是轮流着来的,只是它切换的速度太快,你感受不到!
CPU其实一直不断的在加载QQ的上下文(内存、显卡、硬盘等资源)→执行QQ→保存QQ的上下文→加载IDEA的上下文…
好,我们引申出概念:
1.什么是进程?
“计算机中已运行程序的实体,分配资源的基本单位。”
我们编写的程序,只是指令(比如做加法)、数据(比如一个整数1)和组织形式(比如某个数组,是按顺序排的一系列元素)的描述,当我们下达运行它的命令时,才会产生进程。操作系统按进程为单位分配资源,这些资源包括一片内存、操作系统描述符(所谓文件句柄/文件描述符),安全特性以及CPU状态(视是否在运行存储在寄存器/内存中)等。
总结:进程是一个单位,我们下达运行程序命令时,就可以向操作系统申请领取“一个进程的”大礼包,包含了一段时间内的一部分计算机资源。
2.什么是线程?
“操作系统调度的最小单位”。
在此不对OS这门课做展开,不讨论内核态用户态线程。
我们再设想一个场景。你从操作系统领取了大礼包,它包含了一部分的时间(我感觉生命在流逝)和一部分的计算机资源。但是现在你的代码里需要打开硬盘上的某个文件,或者更过分的是等待某个网络请求(还记得150ms在CPU的眼里是12.5年吗?)……
这是一种严重的浪费!在这个礼包、甚至之后的好多礼包给的时间内,你创建的进程什么都没有做,白白的浪费了系统资源,时间一到CPU就切换到了别的程序,这极大的影响了程序的执行效率。
这时候如果我能把进程拆分为一个个部分,由它们共享这些资源;当第一个部分需要等待某个耗时较长的操作时,其他部分也可以在这份时间里利用这份资源,岂不美哉?
总结:线程是一个更小的单位,“一个进程”的资源,可以由多个“线程”共享,免得出现资源浪费。
3.什么是并发,什么是并行?
Erlang之父简单的介绍了它们的区别:

串行是一个队列一台咖啡机,如果有人匹到了一把Dota,过了一个小时打完回来接咖啡,后面的人都得必须等着;并发是两个队列用一台咖啡机,咖啡机在处理两个队列的人的状态中不断切换,在逻辑上这台咖啡机可以同时处理两个队列;并行则是两个队列用两台咖啡机,有两台咖啡机在同一个时间点处理两个队列,注意,是真正意义上、物理上的同时,这也是多逻辑核心CPU的模型!
走进Java并发编程01