多线程基础
学习资料:
深入浅出多线程 JavaGuide 《并发编程的艺术》 百度233
1,进程与线程
1.1,进程:运行一个程序,就会创建进程。应用程序在内存中分配的空间。让操作系统实现并发。
线程:一个程序(进程)的子任务。实现进程内部的并发。轻量级线程。
1.2,关系:
has a 进程可以包含多个线程,共享进程的资源。
is a 线程是轻量级的进程
2,并发与并行:
并发:同一时间段内,多个任务都在指行(单CPU进程调度)看起来是并行的。
并行:同一时刻,多个任务同时执行,(多核CPU,真正意义上的并行)
3,为什么使用多线程
①消耗资源小。线程切换和调度的成本远小于进程,
②相应速度快。大量并发网络环境,需要多线程机制快速相应。
- 多线程应用场景:Web服务器,专用服务器(游戏)。后台任务(群发邮件)。异步处理。分布式计算。
③CPU利用率高。多核CPU时代,多个核心运行多个线程,减少饥饿。
- 线程越多,利用率就越高? 多线程一定能提高CPU利用率?
使用多线程之前CPU的空闲时间 大于 使用多线程后用于线程切换上下文的时间。两者之差才是提升CPU真正利用在计算上的时间。
- 什么是上下文切换
线程/进程 调度 中时间片流转法,当一个线程时间片用完或者被中断阻塞了,要切换下一个就绪队列中的线程,要保存当前线程的‘现场’
,即线程状态信息,资源信息,程序计数器(记住线程执行到哪了,下次轮到时间片时接着执行)等。同时将要切换进来的线程保存的
‘现场’信息加载进来,接着他上次执行的地方接着执行。
4,线程的生命周期:NEW ,RUNNABLE,BLOCED,WAITING,TIME_WAITING,TERMINATED
①new一个线程的时候,处于NEW状态
②调用start之后处于RUNNABKE状态,可执行态,分为就绪态(等待CPU时间片)和运行态。
③BLOCED:当线程尝试获取锁失败被挂起阻塞时
④WAITING,Object.wait(),Thread.join(),后释放cpu和锁,等待被唤醒。Object.notify(),Object.notifyAll().
⑤TIME_WAITING,超时等待(定时等待)一定时间后回到可执行态。Thread.join(long),Object.wait(long),Thread.sleep(long)
⑥TERMINATED,线程run方法执行完成后。
5,创建多线程方法
继承Thread类,实现Runnable接口,实现Callable接口(有返回函数)
6,多线程带来的问题:
死锁,内存泄漏,线程不安全(数据不同步),
。。。
7,解决多线程同步问题:
既然多线程同一时刻有多个线程访问/修改同一个资源/数据,会出错。那就把资源/数据 锁住就像公共厕所,入坑关门反锁,排队(抢锁机制)入坑。
7.1,锁。 synchronized与Lock
可重入锁-非可重入锁 , 公平锁-非公平锁 , 读写锁-排他锁 , 悲观锁-乐观锁。
常用排他锁:synchronized ,Reentrantlock
7.2,锁的结构:(个人理解,如有误导,多谢指正)
①锁状态(锁标识,)
标识位,一般用一个volatile int遍量 如 private volatile int state;(volatile后面再解释),规定好,state的值为多少对应什么状态
上锁释放锁时对state怎么操作(+1,-1)
②阻塞队列+排队机制(抢锁机制根据锁状态)
③等待/通知机制(释放锁的时候通知其他人现在锁空了,赶紧来枪锁)(这是线程之间通信里的内容,放锁里一起理解,便于比较)
8,CAS操作:
CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值:
-
- V:要更新的变量(var)
- E:预期值(expected)
- N:新值(new)
比较并交换的过程如下:
判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。
所以这里的预期值E本质上指的是“旧值”。
我们以一个简单的例子来解释这个过程:
- 如果有一个多个线程共享的变量
i
原本等于5,我现在在线程A中,想把它设置为新的值6; - 我们使用CAS来做这个事情;
- 首先我们用i去与5对比,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功,
i
的值被设置成了6; - 如果不等于5,说明
i
被其它线程改过了(比如现在i
的值为2),那么我就什么也不做,此次CAS失败,i
的值仍然为2。
- 如果有一个多个线程共享的变量
在这个例子中,i
就是V,5就是E,6就是N。
那有没有可能我在判断了i
为5之后,正准备更新它的新值的时候,被其它线程更改了i
的值呢?
不会的。因为CAS是一种原子操作,它是一种系统原语,是一条CPU的原子指令,从CPU层面保证它的原子性
当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作