前言
大家好,我是楼仔!
为了方便大家学习,我会把所有的系列文章整理成手册,今天给大家整理的是「
J
a
va
并发编程手册」。
这个手册是我去年写了,当时是一边学习一遍整理,肝了我
2
个月,一共
9
篇!
1-4 章是 Java 内存模型,主要提炼程晓明的《深入理解 Java 内存模型》,5 章、6 章、9 章主要提炼 Braian Goetz
的《Java 并发编程实战》,第 7 章是整理的网络博客,第 8 章是提炼我司的多线程项目,代码可以从 Github 下
载,非常有借鉴意义!
这个手册,很多知识虽然并非原创,但是整理后的内容,都是精华浓缩,无论你是面试,还是进阶,这些文章绝对
不会让你失望!
第 1章:Java并发编程基础
主要讲解Java的并发编程的基础知识,包括原子性、可见性、有序性,以及内存模型JMM。
Java
系列说明
从这篇文章开始,我就要正式开始学习Java了,之所以说是从现在开始,是因为前两个月一直在纠结是否转技术栈
(细心的同学可以发现,我之前写的文章,其实和
J
a
va
并没有什么关系),现在已经想清楚了,既然确定要转
J
a
va
技术栈,那就踏踏实实从头开始学吧。
目前的我,可以说是
J
a
va
小白,刚转团队不久,也就接触了
2
个月的
J
a
va
,代码没写几行,既然发现自己
J
a
va
很菜,
那就要列个学习计划,将这块知识好好补补。目前给自己定了一年的学习计划,希望能通过一年的学习,将
J
a
va
的
技能从初阶直接晋级到高阶水平,可能有同学会问
“
我学习
J
a
va
都几年的,都还是中级水平,你花一年就可以晋级到
高阶?
”
,我只想说,我想试试,毕竟工作这么长时间,也掌握了一定的学习方法,相信跟着自己的学习节奏走,应
该不会离目标太远,今天立个Flag,希望一年后不会啪啪打脸【捂脸】
~~
J
a
va
系列的内容主要包括并发编程、
Spring
、
S
p
r
in
g
Boos
t
、
S
p
r
in
g
C
loud
、
T
om
c
at
、
M
y
B
a
ti
s
、
Du
bbo
和虚拟机,然后一些经典书籍的读书笔记等,当这些都掌握到一定深度后,我想我的
J
a
va
技能应该也就差不多
了。
最后想说的是,
J
a
va
很多系列文章,很大一部分是内容整理,之所以要通过文章的形式再写一遍,是因为看过的内
容,如果自己不整理一遍,或者不让程序跑跑,很容易遗忘,所以写文章其实不是目的,主要是重新整理和回顾学
习内容的过程,一方面印象深刻,另一方面,也便于自己后续查阅。
今天废话有点多,我们就从并发编程开始吧!
并发编程基本概念
原子性
一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。
简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。例如
a=1
是原子性操作,但是
a++
和
a
+=1
就不是原子性操作。
J
a
va
中的原子性操作包括:
基本类型的读取和赋值操作,且赋值必须是值赋给变量,变量之间的相互赋值不是原子性操作;
所有引用
r
ef
eren
c
e
的赋值操作;
java.concurrent.Atomic.* 包中所有类的一切操作。
可见性
指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。
J
a
va
提供了
vola
ti
l
e
来保证可见性,当一个
变量被
vola
ti
l
e
修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他
线程读取共享变量时,会直接从主内存中读取。当然,
s
y
n
c
h
r
oni
ze
和
Lock
都可以保证可见性。
s
y
n
c
h
r
oni
z
ed
和
Lock
能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。
因此可以保证可见性。
有序性
即程序执行的顺序按照代码的先后顺序执行。
J
a
va
内存模型中的有序性可以总结为:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个
线程,所有操作都是无序的。前半句是指
“
线程内表现为串行语义
”
,后半句是指
“
指令重排序
”
现象和
“
工作内存主主
内存同步延迟
”
现象。
在
J
a
va
内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序不会影响单线程的运行结 果,
但是对多线程会有影响。
J
a
va
提供
vola
ti
l
e
来保证一定的有序性。最著名的例子就是单例模式里面的
DC
L
(双重检
查锁)。另外,可以通过
s
y
n
c
h
r
oni
z
ed
和
Lock
来保证有序性,
s
y
n
c
h
r
oni
z
ed
和
Lock
保证每个时刻是有一个线程
执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
为了让大家更好理解可见性和有序性,这个就不得不了解
“
内存模型
”
、
“
重排序
”
和
“
内存屏障
”
,因为这三个概
念和他们关系非常密切。
内存模型
JMM
决定一个线程对共享变量的写入何时对另一个线程可见,
JMM
定义了线程和主内存之间的抽象关系:共享变
量存储在主内存
(
M
a
in
M
em
or
y
)
中,每个线程都有一个私有的本地内存(
Loca
l
M
em
or
y),本地内存保存了被
该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的
变 量。
对于普通的共享变量来讲,线程
A
将其修改为某个值发生在线程
A
的本地内存中,此时还未同步到主内存中去;而线
程
B
已经缓存了该变量的旧值,所以就导致了共享变量值的不一致。解决这种共享变量在多线程模型中的不可见性
问题,可以使用
vola
ti
l
e
、
s
y
n
c
h
r
oni
z
ed
、
final
等,此时
A
、
B
的通信过程如下:
首先,线程
A
把本地内存
A
中更新过的共享变量刷新到主内存中去;
然后,线程
B
到主内存中去读取线程
A
之前已更新过的共享变量。
JMM
通过控制主内存与每个线程的本地内存之间的交互,来为
j
a
va
程序员提供内存可见性保证,需要注意