并发编程Day01
多线程
得分点:线程与进程的关系、为什么使用多线程
线程时操作系统调度的最小单元,它可以让一个进程并发地处理多个任务,也叫轻量级线程。所以,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、局部变量,并且能够共享进程内的资源。由于共享资源,处理器便可以在这些线程之间快速的切换,从而让使用者感觉这些线程在同时执行。总的来说,操作系统可以同时执行多个任务,每个任务就是一个进程。进程可以同时执行多个任务,每个任务就是一个线程。一个程序运行之后至少有一个进程,而一个进程可以包含多个线程,但至少要包含一个线程。
使用多线程的原因主要有一下几点:
- 更多的CPU核心,程序使用多线程技术,就可以将技术逻辑分配到多个处理器核心上,显著减少程序的处理时间,从而随着更多的处理器核心的加入而变得有效
- 更快的响应时间,针对复杂的业务编写出复杂的代码,使用多线程技术,就可以将数据一致性不强的操作派发给其他线程处理,如:上传图片、发送邮件、生成订单等。
- 更好的编程模型。Java为多线程编程提供了良好且一致的编程模型,使得开发人员能够更加专注于问题的解决
怎么保证线程安全
原子类、volatile、锁。
Java保证线程安全的方式有很多种,其中较为常用的有三种,按照资源占用情况由轻到重排序,这三种保证线程安全的方式分别是:原子类、volatile、锁。
- concurrent.atomic包,这个包中的原子操作提供了一种用法简单、性能高效、线程安全地更新一个变量的方式,无论原子更新哪种类型,都要遵循“比较和替换”规则,即比较要更新的值是否等于期望值,如果是则更新,如果不是则失败。
- volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,从而可以保证单个变量读写时的线程安全。可见性问题是由处理器核心的缓存导致的,每个核心均有各自的缓存,而这些缓存均要与内存进行同步。volatile具有如下的内存语义:当写一个volatile变量时,该线程本地内存中的共享变量的值会被立刻刷新到主内存;当读一个volatile变量时,该线程本地内存会被置为无效,迫使线程直接从主内存中读取共享变量。 原子类和volatile只能保证单个共享变量的线程安全,锁则可以保证临界区内的多个共享变量的线程安全
- 锁则可以保证临界区内的多个共享变量的线程安全,Java中加锁的方式有两种,分别是synchronized关键字和Lock接口。synchronized是比较早期的API,在设计之初没有考虑到超时机制、非阻塞形式,以及多个条件变量。若想通过升级的方式让它支持这些相对复杂的功能,则需要大改它的语法结构,不利于兼容旧代码。因此,JDK的开发团队在1.5新增了Lock接口,并通过Lock支持了上述的功能,即:支持响应中断、支持超时机制、支持以非阻塞的方式获取锁、支持多个条件变量(阻塞队列)
进程与线程
得分点:开销、并发性、内存、地址空间
进程与线程主要的差别在于它们是不同的操作系统资源管理方式
- 进程有独立的地址空间,线程有自己的堆栈和局部变量,但是线程之间没有独立的地址空间。
- 进程与线程切换时,需要切换进程和线程的上下文,进程的上下文切换时间开销远远大于线程上下文切换时间,消耗资源较大,效率要差一些、
- 进程的并发性较低,线程的并发性较高。
- 每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序员提供多个线程执行控制。
- 系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程比多线程更健壮
线程与协程
得分点:同步、异步、并行、并发
- 线程是操作系统的资源,线程的创建、切换、停止等都非常消耗资源,而协程不需要调用操作系统的功能,编程语言自身就能完成,所以协程也被称为用户态线程,协程比线程轻量很多
- 线程在多核环境下是能做到真正意义上的并行,而协程是为了并发而产生的
- 一个具有多个线程的程序可以同时运行几个线程,而协程却需要彼此协作运行
- 线程都是同步机制,而协程则是异步
- 线程是抢占式,而协程是非抢占式,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有个一个协程拥有运行权,相当于单线程的能力
- 操作系统对于线程开辟数量限制在千的级别,而协程可以达到上万的级别。
并行与并发
- 并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
- 并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。