一、基本概念
并发:多个计算同时进行。
其实上面这个定义并不准确,容易讲并发跟并行理解成同一个概念,其实不然,有人这么解释,我觉得很通俗易懂:并发,就像一个奶妈(CPU)给多个孩子喂奶,轮换着每个人都吃一口,表面上每个孩子都在吃饭,而并行,就像多个奶妈(CPU)给多个孩子喂奶,实际上每个孩子就是都在吃饭。
进程:执行中程序的实例。
线程:进程中的一个执行任务,负责当前进程中程序的执行。一个进程至少有一个线程,可以有多个线程。
在《Java编程思想》中,有这么一段话,阐述了如何利用线程完成多任务的处理:我们可以将程序划分为多个分离的、独立运行的任务。通过多线程机制,这些独立任务(子任务)中的每一个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务,但你的程序使得每个任务都好像有自己的CPU一样。
简而言之,我们通过任务划分让一个线程执行一个任务,通过线程间的切换实现多任务处理。
二、并发编程的问题
明确一个概念,在程序中单行、单条语句的执行未必都是原子的。以讲义中的银行存款为例,执行a++操作可能包含三个原子操作,取a,计算a+1,写回a。那这样,若两个线程都对同一个a执行a++操作,我们期望的返回值是a+2,可事实上由于三个原子之间的交错,返回值可能是a+1,这也暴露的多线程中的一个问题,竞争。线程之间的竞争极有可能干扰到程序的执行结果,可我们也能窥探出解决这一问题的思路,我觉得可以分为两类:一类是限制读写,一类是干扰线程切换,保证某些操作的原子性。
三、保证并发的安全性
讲义中给出了四种增强线程安全性的方法:
!限制数据共享
!共享不可变数据
!共享线程安全的可变数据
!同步机制:通过锁的机制共享线程不安全的可变数据,变并行为串行。
一类导致线程不安全的行为就是由于操作的非原子性,多个线程同时读写有很大的隐患,解决方法对应于前三种:第一,我不共享数据,这样就不能都读都写了吧,但这样往往不能满足编程要求;第二,我共享数据,但我只共享不可变的数据,你们只读不写,也就没啥问题了吧;第三,我共享数据,也共享可变数据,但我只共享线程安全的可变数据。
但是,线程安全的可变数据并非绝对的线程安全,它智能保证某个操作是原子的,但是不能保证多个操作放在一起仍旧是原子的。换句话说,其原子操作的范围不够大,有时候不能满足程序的要求,这时候,第四步,也就是第二类,主动干扰线程切换,自定义线程原子操作,进而使得程序是线程安全的。