指的是多个线程操作同一个资源,多线程来交替的操作同一个资源(CPU,内存资源)。
临界资源和临界区:
临界资源:一个时刻只允许一个线程访问,一个线程在访问临界资源时,其他线程是不能访问的,临界资源是不可剥夺资源,OS不能阻止资源的独享行为。
临界区:是一个线程中访问临界资源的代码片段。
临界区的使用规则:“空闲让进,忙则等待,有限等待,让权等待”。
(1)空闲让进:临界资源空闲时一定要让进程进入,不发生“互斥礼让”行为。
(2)忙则等待:临界资源正在使用时外面的进程等待。
(3)有限等待:进程等待进入临界区的时间是有限的,不会发生“饿死”的情况。
(4) 让权等待:进程等待进入临界区是应该放弃CPU的使用。
线程安全:
如果是一个操作的序列,在单线程执行和多线程执行的情况下,最终得到结果永远是相同的,把这个操作序列称之为线程安全的,反之,则是非线程安全的。
并发的特征:
- 原子性 如果一个操作是不可分割的,那这就是一个原子操作。相反,如果一个操作是可以分割的,那么他就是非原子操作,(a++是非线程安全操作,它是可拆分的,找到a,a执行加一操作,赋值a)。
- 可见性 一个变量被多个线程共享,如果一个线程修改了这个变量的值,其他的线程会立马得知这个修改,我们称这个操作具有可见性。
- 有序性
有序性两方面表现:
1、在一个线程内存观察,所有的操作都是有序来的,所有的执行指令按照”串行“(as-if-serial)
2、在线程间观察,从一个线程观察其他线程,则线程的执行是交替执行,是正序的。
线程局部变量表:
在Java内存模型中,虚拟机栈(本地方法栈)是线程私有,存储的是局部变量表、动态链接、方法的出口.dll 动态链接文件 ,虚拟机栈中存在一个个帧栈(一个个方法的调用,开辟栈帧)。
对象是存在于堆中:
局部变量表:主要存储的数据。
基本的数据类型:存储在堆中的数据会拷贝一份到局部变量中 Integer。
对象的引用:局部变量表中不存储对象,对象存在堆中,在局部变量中只存在对对象的引用地址。
内存模型:
堆内存中的对象和基本数据类型的备份,称为主内存(main memory),把上面所说的栈内存中用于存储变量的部分内存,称为本地内存(local memory)(或叫工作内存)。
1、Java线程对于变量的操作,都是在自己的工作内存中进行的,线程不会直接读写主内存的变量。
2、不同的线程无法访问对方线程工作内存中的变量。
3、线程间变量的传递,需要主内存来完成的。
例如:
主存存在一个变量num = 15;当线程A和线程B都来操作该变量,当前线程A 执行num = 14;将结果写到主内存,此时线程B已经读取了count ,此时修无法读取最新值,会存在问题。
Java内存模型中对数据的8种操作:
- lock (锁定) :作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock (解锁) :作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read (读取) :作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用。
- load (载入) :作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use (使用) :作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign (赋值) :作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store (存储) :作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write (写入) :作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。