文章目录
简介
*程序并不难,最主要的背后的思想。
可能大学刚毕业的时候,那时候还不知道进程,线程的概念,后面才知道理论基础的重要性,哈哈,第一次遇到并发的时候,还以为我电脑坏了…
当时还是觉得挺Odd(奇怪的)现象
*
代码
package com.wuyuhong;
public class MyThread extends Thread {
@Override
public void run() {
//子线程
for (int i = 0; i < 2000; i++) {
System.out.println("我在听歌-->" + i);
}
}
public static void main(String[] args) throws Exception {
//main 主线程
//创建子线程
MyThread myThread = new MyThread();
//start()开启线程,并执行run方法
myThread.start();
//run 调用同步方法
//myThread.run();
for (int i = 0; i < 2000; i++) {
System.out.println("我在学习" + i);
}
}
}
console-1结果
......
我在学习516
我在学习517
我在学习518
我在学习519
我在学习520
我在学习521
我在听歌-->804
我在听歌-->805
我在听歌-->806
我在听歌-->807
我在听歌-->808
我在听歌-->809
.....
我在学习522
我在学习523
我在学习524
我在学习525
想一想
为什么会出现这样的情况呢?看起来都是同步的代码,为什么交替在执行呢?
不应该先执行完"我在听歌",然后再执行"我在学习"吗?
好吧,其实这里是并发了,到这里肯定很多人都知道
但是为什么需要这种并发行为呢?
为什么不顺序执行呢?(其实早期的批处理程序(单进程单线程),就是顺序执行的)
再试一试
我灵光一闪把代码再次改造了一下,结果还是符合我的预期的,可能更能突出我后文想传达的意思
package com.wuyuhong;
public class MyThread extends Thread {
@Override
public void run() {
//子线程
for (int i = 0; i < 20000; i++) {
while (true) {
System.out.println("我在听歌-->" + i);
}
}
}
public static void main(String[] args) throws Exception {
//main 主线程
//创建子线程
MyThread myThread = new MyThread();
//start()开启线程,并执行run方法
myThread.start();
//run 调用同步方法
//myThread.run();
for (int i = 0; i <= 2000; i++) {
System.out.println("我在学习" + i);
if(i==2000){
System.exit(0);
}
}
}
}
console-2 结果
....
我在听歌-->0
我在听歌-->0
我在听歌-->0
我在听歌-->0
我在听歌-->0
我在听歌-->0
我在听歌-->0
Process finished with exit code 0
//最开始我没加System.exit(0),因为控制台打印太快了,我专门判断了主线程是否执行了
Process finished with exit code 0
这里最后执行了exit(0),说明主线程是执行了的,并且执行完毕了
开始分析
以下将可能涉及这些内容(也是大家可以研究的方向),以下内容仅供参考,因为毕竟我不是写windows和写linux的人,我只能通过现象来看事物的本质,要是我写操作系统,我就把调度策略写成FIFO,让大家都能爽一下
以下内容 参考 《操作系统导论》,我看的是中文版,发现文中确实有很多描述不清楚的地方,
但是大致的方向是差不多的
操作系统
并发和并行
受限直接运行
时钟,时间片
调度策略(指标--周转时间,响应时间)
用户线程和内核线程
什么是并发
单核cpu,CPU资源是有限的,在同一时刻只会处理一个任务(只运行一个进程),在一段时间中交替处理了多个任务,这个就是并发
为什么需要并发
1从历史来说:
早期的操作系统就是一组函数库(函数库 ex:C语言的printf,sizeof,fork...),
然而当时的程序猿写的程序叫做批处理程序,早期的操作系统一次只能运行一个进程(主线程)
比如程序员A写了一个程序A,程序猿B写了一个程序B,放在早期的操作系统上,
比如两个人同时都写完了,都需要运行,
这时假如A先来到电脑前,执行A程序,那么B程序猿就必须等在那里,
等到A的程序跑完了,才能跑自己的程序B,程序员B当时的心情肯定很气也很无奈
这时程序员B就有了需求:能不能大家的程序都一起运行,大家都运行一会儿,
A运行一会儿然后暂停后运行B,
B程序运行运行一会儿后运行A,这样公平一点
这里的需求就是:想要并发,这样就出现了时分操作系统
(运行一个进程一段时间,然后再运行另一个进程一段时间),也就是上面第一个程序运行的行为,
虽然上面是线程,但道理类似的
简单来说,在一段时间中,大家都交替使用一下CPU资源,而且当A任务执行I/O系统调用时,B任务还可以执行,这个就是并发的优势,节约时间,提高CPU利用率
从调度策略来说的话,这里看起来像是轮转策略,如果更深层次,可以从周转时间和响应时间来分析
轮转策略:
这部分是自己的一些感悟,随便写的,可以不看
其实优化的是响应时间,这个例子中,如果AB程序跑在分时操作系统上,
最直观的感受就是程序猿B能很快的看到程序运行的部分结果,
比如打印日志log(1+1=2)...log(2+1=3)...,
如果都是计算的话,虽然最后的总时间差不多,但程序员B此时肯定就不会发恼骚了,
但假如加入I/O分析的话,并发将会有很大的优势
假如我想while(true)独占cpu资源,我想耍流氓
假如我程序中while(true)执行一个程序,我能把我自己的电脑弄崩溃吗,我能听不了歌,我能干不了其他事情了吗?
就像程序二的例子一样,最终主线程还是执行完了
这里其实是操作系统 中 受限直接运行 的原理,为了保护计算机和防止OS自己永远获取不了CPU控制权
要提供一个怎样的机制?来限制这种流氓程序的行为呢?
时钟
时钟每隔一段时间(时钟频率),产生一个时钟中断,
让OS获得cpu的控制权,从而可以选择停止或杀死当前进程,从而运行另一个进程。
(这里也是并发的特性)
时间片
那时间片是什么呢?
其实时间片由 N(N>=1)个时钟中断周期组成,是OS分配给进程的使用CPU的一段时间,
我自己的理解,由于时间中断产生频率会很快,一般都是ms级别的,
如果每一次时钟中断产生,都让OS来切换进程,那么开销还是挺大,
由多个时钟中断的周期时间,来组成一个时间片,这样还好一点
官方解释:
分时操作系统将CPU的时间划分成若干个片段,称为时间片。操作系统以时间片为单位,
轮流为每个终端用户服务。
当OS获得了CPU控制权,是继续运行当前线程,还是运行另一个线程呢
这里就是,OS调度策略了,不想写了
总结
主要从
1.为什么需要并发,提高cpu利用率,提高响应时间(想象一下假如你的Server服务没并发,客户的请求的响应时间将会糟糕透了,因为你的程序将有大量的CRUD I/O操作,因为CRUD的本质就是读写磁盘)
2.操作系统保护机制(也就是受限直接运行ex:while(true)无限制的占用cpu)
自身研究操作系统也不太久,欢迎大家评论和指正