温故知新 线程
项目中常会用到定时器任务调度(线程知识)
本人原电子系后转行到软件,所以对计算机原理方面颇感兴趣!
谈线程钱必须知道进程,CPU调用程序,以进程体系,轮询。
时间片(CPU定轮询周期),每个进程周期一样,(如上图)当前执行进程1指令。执行到一半,操作系统OS (管理计算机硬件与软件资源的计算机程序,同时也是计算机系统的内核与基石)给它分配时间到了,收回。
它把这个进程挂起,暂时保存起来。OS就去按顺序遍历,执行进程2,...........直到执行到进程8的时间片用完了,挂起进程8。接着执行进程1,恢复现场,接着执行。
注意:进程调度,程序之间可以切换。cpu执行太快会给人错觉误以为程序都是并行(同时运行);其实cpu定轮询周期(时间片),每个进程运行/执行的时间固定,当时间到了进程仍没执行完,当前进程挂起,执行下一条进程(直到进程8结束),当OS再次执行时,又回到进程1,恢复现场,接着执行;
知道了进程后,开始谈谈线程了:
一个进程可包含多个线程,也就是线程是OS(操作系统)可以执行的最小单位;(查服务器主机几核几线程 【cmd->wmic->cpu get *】查看对应的NumberOfCores和NumberOfLogicalProcessors )
线程数=Ncpu/(1-阻塞系数)
Ncpu---CPU数 阻尼系数---(0到1之间)
线程数大于CPU核数(并行):
并行:多核情况下,各自CPU执行各自内容,可以同时执行;
并发:多核情况下,某一个核中执行个线程,此时就产生并发,分片执行,轮着顺序执行。也称为交替执行。
上图摘自网络:(一图胜千文)
JVM在多线程时,访问公用变量(堆、方法区。。。)的每个线程是自由的,但有线程存在私有变量会有私有的栈来供他存放(保证了线程间隔离的安全)
切记创建线程数非常多,可能造成内存溢出;--------因为每个线程要分配单独内存空间,直接占用。(可以理解为独食大王,宁占着不让位)
多线程创建方式:
1) 继承Thread
2) 实现Runnable,无返回值,无异常接收
3) 实现Callable,返回值,接收异常,多了一些对象 Future、FutureTask
4) 线程池
多线程状态:
1,新建new;调用start()方法,注:所有的线程都从start开始(仅一次且由OS决定)
2,可运行态Runnable; 1, 当时间片用完,运行态会调用 yiled()转到可运行态;注:【yiled() 会强制退回到就绪状态,代码主动调用。主动把CPU让出来,让其他线程去执行。俗称挂起】 ,2,当OS选中时,会从可运行态转运行态;
3,运行态Running;Run方法执行完成,线程就结束,释放资源。(切记不要本线程调用---那将毫无意义)
4,阻塞态blocked; 对于阻塞态和运行态,可运行态 典型的稳定三角关系:
运行态变阻塞:
sleep() 【它会把CPU让出来,先阻塞等待(我先睡会你们继续)】醒来后转为可运行态;
join() 【运行快的所有线程都等待,只到最慢那个线程执行完成,(有福同享,有难共当)】
wait() 【当前线程优先占用,其他线程停止,(有肉我先吃)】 当调用interrupt()或notify()或notifyAll()被唤醒后仍处于阻塞态,可转为可运行态;’
加锁Synchronized 【关键字,同步,同步锁,代码块,多行代码被加锁】 原子性操作,其他线程无法执行(特别注意CPU不是立即执行,而是轮询到它在执行。),切记加锁范围越小越好!
阻塞态变可运行态:获得锁 sleep时间到了
5,终止态Dead ;run()结束
特别要重视线程池:
通俗讲就是线程的集合,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。(可以理解为 孟尝君食客三千待用);
作用:防止多线程运行,系统不断闭合新线程,系统资源不断消耗,线程切换频繁,导致系统崩溃 (蓝屏的真好喝!)
线程池可参见博客大神闯天创世纪的博文:https://www.cnblogs.com/jiawen010/p/11855768.html
多线程并发在项目中常见安全问题:
甩卖问题:
多个窗口卖固定的货: m个窗口 卖n件货
线程m个 甩卖过程 n递减(假设没退换,卖出责任不究)
其实主要是当n=1时问题出来了,要解决超卖现象:解决办法-------加锁(执行结束锁就释放),在加锁范围内不能高并发,只能一个对象去使用。
在商品秒杀系统时,有条件有选择的在读操作上加锁,库存小于一个量时开始加锁,让购买者排队;用一个队列缓存,将多线程变为单线程读写;