Odd-Java-2 Thread-我的线程交替执行了

简介

*程序并不难,最主要的背后的思想。

可能大学刚毕业的时候,那时候还不知道进程,线程的概念,后面才知道理论基础的重要性,哈哈,第一次遇到并发的时候,还以为我电脑坏了…
当时还是觉得挺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)

自身研究操作系统也不太久,欢迎大家评论和指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值