聊聊并发
- 你真的了解并发码
- 多线程和并发
- 多线程和多进程
- 线程一定快吗
- 学习并发的四个阶段
- 学习目标
- 适合人群
- 推荐推荐
简介
编写正确的程序很难,而编写正确的并发程序则难上加难。
与串行程序相比,在并发程序中存在更多容易出错的地方。那么,我们为什么还要编写并发程序呢?
线程是Java语言中不可或缺的重要功能,它们能使复杂的异步代码变得更简单,从而极大地简化了复杂系统的开发。
此外,使用线程可以充分发挥多处理器的强大计算能力。随着处理器数量的持续增长,如何高效地使用并发正变得越来越重要。
并发简史
早期的计算机不包含操作系统,在这种裸机环境中每次只能运行一个程序,浪费计算机资源。
操作系统的出现使得计算机每次能运行多个程序,不同的程序在单独的进程
中运行,操作系统为各个独立的进程分配各种资源,包括
内存、文件句柄以及安全证书等。在不同的进程之间可以通过一些粗粒度的机制通信,包括:套接字、信号处理器、共享内存、信号量以及文件等。
线程允许在同一进程中同时存在多个程序控制流。线程会共享进程范围内的资源,例如内存句柄和文件句柄,
但每个线程都有自己的程序计数器(Program Counter)、栈以及局部变量等。
在同一个程序中的多个线程可以被同时调度到多个CPU 上运行。
线程也被称为轻量级进程。在大多数现代操作系统中,都是以线程为基本的调度单位,而不是进程。
由于同一个进程中的所有线程都将共享进程的内存地址空间,因此这些线程都能访问相同的变量并在同一个堆上分配对象。
这就需要实现一种比在进程间共享数据粒度更细的数据共享机制。如果没有明确的同步机制
来协同对共享数据的访问,那么
当一个线程正在使用某个变量时,另一个线程可能同时修改这个变量,这将造成不可预测的结果。
线程的优势
- 线程可以有效地降低程序的开发和维护成本,同时提升复杂应用程序的性能。
- 线程能够将大部分异步工作流转换成串行工作流,因此能更好地模拟人类的工作方式和交互方式。
- 降低代码的复杂度,使代码更容易编写、阅读和维护。
- 在GUI应用程序中,线程提供用户界面的灵敏度。
- 在服务器应用程序中,可以提升资源利用率以及系统吞吐率。
- 简化JVM的实现,垃圾收集器通常在一个或多个专门的线程中运行。
线程带来的风险
- 安全性问题
- 活跃性问题
- 性能问题
线程安全性是非常复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生奇怪的结果。
在下面的UnsafeSequence 类中将产生一个整数值序列,该序列中的每个值都是唯一的。1
2
3
4
5
6
7
8
9
10
11
12public class UnsafeSequence {
private int value;
/**
* 返回一个唯一的数值
* @return
*/
public int getValue() {
return value++;
}
}
在单线程环境中,这个类能正常工作,但是在多线程环境中则不能。
UnsafeSequence 问题在于,递增运算value++ 看上去是单个操作,实际上它包含三个独立的操作:
读取value,将value值加1,将计算结果写入value。由于运行时可能多个线程之间的操作交替执行,结果可能返回了相同值。
// 画图
这是一种常见的并发安全问题,称为竞态条件(Race Condition)。
要使多线程程序的行为可以预测,必须对共享变量的访问操作进行协同,防治多线程之间发生彼此干扰。
Java提供了各种同步机制
来协同这种访问。通过将getValue 方法改为一个同步方法,可以修复UnsafeSequence 中的错误。1
2
3
4
5
6
7/**
* 返回一个唯一的数值
* @return
*/
public synchronized int getValue() {
return value++;
}
线程无处不在
Thread t1 = new Thread() {
@Overvide
public void run() {
}
};
t1 是指向Thread 实例的一个引用,并不是一个真正的线程。
t1.start();
1.Java程序的main方法是一个线程,是被JVM启动时候调用,线程的名字是main。
2.实现一个线程,必须创建Thread实例,Override run方法,并且调用start方法。
3.当
为什么要学习并发编程
并发优点:
- 发挥出多处理器的能力
- 建模的简单性
- 异步事件的简化处理
- 响应更加灵敏的用户界面
并发的缺点
- 线程安全性问题
- 活跃性问题
- 性能问题
学习并发的四个阶段
- 熟练掌握API,能够完成并发编程
- 熟读API源码,掌握其原理
- 理解Java虚拟机的内存模型
- 操作系统对并发对支持