Java并发编程笔记一

Java个人学习笔记

背景

甲方项目提出前端页面增加一个功能,需要将数据库的100万(百万两级)条数据,在限定8秒之内(用户体验)导出到指定数量的Excel表.
采用方案一:C++或者golang或者C等等,但是后端技术栈是SpringClound微服务,这样胶水式操作不易维护。
采用方案二:Java操作,采用单线程一行一行的读取后写入Excel,那大概率会超时。
采用方案三:使用Java多线程。

分析项目需求 Java多线程不仅是实际项目需要的,还是面试笔试重点。

进程和线程

进程

进程可以理解为运行的一个程序(QQ,微信,网易音乐,Word,运行中的SpringBoot jar包),由PCB(进程控制块)、程序段、数据段组成。可以查看任务管理器重的进程如下
进程示意图
一个进程在OS中的资源,包含了CPU、内存、磁盘、网络、I/O等等。
OS支持多任务模型,会有类似上图 可以同时运行多个程序,对比多道程序概念

每个进程生命周期都会有就绪态,运行态,阻塞态,新建态PCB,终止态五种状态,但前三种为基本态,时间占比较长。

一个进程至少包含一个线程(主线程)。

线程

一个进程的运行开销重,为了更好的使多道程序并发执行。

  1. 线程是CPU使用的基本单元,由线程ID、程序计数器、寄存器集合和栈组成

  2. 进程由一个或多个线程组成:创建一个进程会伴随着创建一个线程(主线程)

  3. 调度切换:线程上下问切换比进程上下文切换快

创建进程:需要为进程划分一整块完整内存,需要大量初始化操作,比如要把内存分段(堆栈、正文区等)。

创建线程:只需要确定PC指针和寄存器的值,并且给线程分配一个栈用于执行程序,同一个进程的多个线程间可以复用堆栈。

多线程进程

一个进程可以划分为多线程(并发)的重要前提:通过线程之间的通信,保证数据原子性、可见性、有序性

原子性(atom)

一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行。在 Java 中,对基本数据类型的变量的读取和赋值操作是原子性操作。

保证原子性

  1. synchronized 关键字定义同步块或者同步方法保障原子性
  2. Lock接口保证原子性
  3. Atomic类型保障原子性

可见性

当一个线程修改了共享变量的值,其他线程能够看到修改的值
JMM通过共享内存实现线程通信

volatile变量和普通变量
volatile变量强行保证了被一个线程修改的变量的数据变量,被刷入内存。代码是一样的,所以其他线程也有volatile变量,但此时他们的volatile变量不生效

保证可见性

  1. volatile 关键字标记内存屏障保证可见性
  2. synchronized 关键字定义同步代码块或者同步方法保障可见性
  3. Lock接口保障可见性
  4. Atomic类型保障可见性
  5. final关键字保障可见性

有序性

程序执行的顺序按照代码的先后顺序执行
JVM存在指令重排,无关系的数据之间的初始,是没有顺序的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class myClass {
public static void main(String[] args) {
......
}
int a = 0;
boolean b = false;
public void A(){
a = a+2;
b = true;
}
public void B(){
if(b){
a = a+1;
}
}
}

如果上述有多个线程在执行,因为a和b之间在A中执行是无关联的,不同于B。 那么在A中经过指令重排序,可能先执行b = true;当还没执行a=a+2;时。另一个线程读取到b的true状态,拿着还是a=0;的情况就开始做a=a+1;
解决办法:使用volitile关键字 public int volitile a;

JMM(Java Memory Model,Java内存模型)

JMM内存模型

还有部分笔记后续补上,先赶项目去了。。。


Java并发编程笔记一
http://oowatermelon.github.io/OoWaterMelonS/2022/10/01/Java并发编程笔记一/
作者
OoWaterMelonS Shao
发布于
2022年10月1日
许可协议