java并发基础

57 阅读2分钟

CPU、内存、I/O 设备的速度是有极大差异的,为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了贡献,主要体现为:

  • CPU 增加了缓存,以均衡与内存的速度差异;// 导致可见性问题
  • 操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;// 导致原子性问题
  • 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。// 导致有序性问题

原子性, 可见性,有序性

原子性:对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

int a=1;//基本数据类型变量赋值保证原子性
int b=a;//变量间赋值不保证原子性,包含2个操作 1.a=1 2.b=1 虽然这2个操作都是原子性操作,但是合起来就不是原子性操作了
a=a+1;  // 包括3个操作:读取a的值,进行加1操作,写入新的值。
a++;    // 同上

//上面4个语句只有语句1的操作具备原子性。

Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。

可见性:

volatile关键字来保证可见性。

  • 当一个共享变量使用volatile关键字修饰时,保证其每次修改都会从工作内存更新到主存,其他线程需要读取时每次都能拿到最新值。

  • 普通共享变量无法保证可见性,当一个变量修改后,什么时候被写入主存是不确定的,不能及时更新到主存中,其他线程读取到的可能还是旧值。

通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

有序性

在Java里面,可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。当然JMM是通过Happens-Before 规则来保证有序性的。