比方电脑里你安装的qq,暴风,迅雷他们都可以单独独立运行,那么我们就可以说qq是一个进程,暴风也是一个进程,迅雷更是一个进程
而要说到什么是线程,线程是运行在进程里里的程序
举例qq(线程),我们能同时用qq软件进行聊天,听音乐,为什么呢,这就是线程的优点所在,单独运行但又不互相干扰,创建线程有两种方法,线程一定要复写run方法,用start启动线程
使用Thread创建线程:
public MyThread extends Thread{
public void run(){
for (int i=0i<20i++){
System.out.println(
“my Thread i value: ”+i)
}
使用Runnable创建线程:
public MyThread implements Runnable{
public void run(){
for (int i=0i<20i++){
System.out.println(
“my Thread i value: ”+i)
}
对于这两种方法,实现线程,但是一般情况下我们都用第二种
因为第一种是用继承的关系,而第二种为实现接口
但是相对于第一种,第二种我们还可以另外继承类,来扩展功能,所以编程序时比较趋向于用实现的方法写线程
给你一个完整的例子:
class MyThread implements Runnable{
private int ticket = 5 // 假设一共有5张票
public void run(){
for(int i=0i<100i++){
if(ticket>0){ // 还有票
System.out.println(Thread.currentThread().getName()+"卖票:ticket = " + ticket-- )
}
}
}
}
public class SyncDemo01{
public static void main(String args[]){
MyThread mt = new MyThread() // 定义线程对象
Thread t1 = new Thread(mt) // 定义Thread对象
Thread t2 = new Thread(mt) // 定义Thread对象
Thread t3 = new Thread(mt) // 定义Thread对象
t1.start()
t2.start()
t3.start()
}
}
这是一个三线程:
三个线程都start,所以他们可以同时运行,但是又由于电脑cpu只有一个,只能运行一个线程,那么这三个线程就会去抢,谁抢到谁就线运行,start表示开始运行run方法,导致三个线程共享5个车票,你会发现每次运行结果不一样,这就是抢的结果,如果把车票写在run方法里就不一样了
Thread-0卖票:ticket = 5
Thread-0卖票:ticket = 4
Thread-0卖票:ticket = 3
Thread-2卖票:ticket = 2
Thread-2卖票:ticket = 1
结果二:
Thread-0卖票:ticket = 5
Thread-0卖票:ticket = 4
Thread-0卖票:ticket = 3
Thread-1卖票:ticket = 2
Thread-0卖票:ticket = 1
反正很多答案,自己试。
第一个问题,不管是创建进程或者创建线程都不会阻塞,创建完毕马上返回不会等待子进程或者子线程的运行第二个问题
首先进程和线程是不一样的
多进程时,父进程如果先结束,那么子进程会被init进程接收成为init进程的子进程,接下来子进程接着运行,直到结束,init进程负责取得这些子进程的结束状态并释放进程资源。而如果是子进程先结束,那么父进程应当用wait或者waitpid去获取子进程的结束状态并释放进程资源,否则子进程会成为僵死进程,它占用的进程资源不会释放
多线程时,如果父线程或者说你讲的main结束时使用return或者exit或者处理完毕结束,那么整个进程都结束,其他子线程自然结束。如果main结束时使用的是pthread_exit那么只有父线程结束,子线程还在运行。同样对于子线程结束时如果调用了exit,那么整个进程包括父线程结束,如果调用了pthread_exit或者正常结束,那么只有子线程结束。
另外子线程结束时如果没有分离属性,其他线程应当使用pthread_join去获取线程结束状态并释放线程资源,如同进程里的wait和waitpid
调度策略值得是大家都在ready时,并且CPU已经被调度时,决定谁来运行,谁来被调度。
两者之间有一定矛盾。
响应的优化,意味着高优先级会抢占优先级,会花时间在上下文切换,会影响吞吐。
上下文切换的时间是很短的,几微妙就能搞定。上下文切换本身对吞吐并多大影响, 重要的是,切换后引起的cpu 的 cache miss.
每次切换APP, 数据都要重新load一次。
Linux 会尽可能的在响应与吞吐之间寻找平衡。比如在编译linux的时候,会让你选择 kernal features ->Preemption model.
抢占模型会影响linux的调度算法。
所以 ARM 的架构都是big+LITTLE, 一个很猛CPU+ 多个 性能较差的 CPU, 那么可以把I/O型任务的调度 放在 LITTLE CPU上。需要计算的放在big上。
早期2.6 内核将优先级划分了 0-139 bit的优先级。数值越低,优先级越高。0-99优先级 都是 RT(即时响应)的 ,100-139都是非RT的,即normal。
调度的时候 看哪个bitmap 中的 优先级上有任务ready。可能多个任务哦。
在普通优先级线程调度中,高优先级并不代表对低优先级的绝对优势。会在不同优先级进行轮转。
100 就是比101高,101也会比102高,但100 不会堵着101。
众屌丝进程在轮转时,优先级高的:
初始设置nice值为0,linux 会探测 你是喜欢睡眠,还是干活。越喜欢睡,linux 越奖励你,优先级上升(nice值减少)。越喜欢干活,优先级下降(nice值增加)。所以一个进程在linux中,干着干着 优先级越低,睡着睡着 优先级越高。
后期linux补丁中
红黑树,数据结构, 左边节点小于右边节点
同时兼顾了 CPU/IO 和 nice。
数值代表着 进程运行到目前为止的virtual runtime 时间。
(pyhsical runtime) / weight * 1024(系数)。
优先调度 节点值(vruntime)最小的线程。权重weight 其实有nice 来控制。
一个线程一旦被调度到,则物理运行时间增加,vruntime增加,往左边走。
weight的增加,也导致vruntime减小,往右边走。
总之 CFS让线程 从左滚到右,从右滚到左。即照顾了I/O(喜欢睡,分子小) 也 照顾了 nice值低(分母高).所以 由喜欢睡,nice值又低的线程,最容易被调度到。
自动调整,无需向nice一样做出奖励惩罚动作,个人理解权重其实相当于nice
但是 此时 来一个 0-99的线程,进行RT调度,都可以瞬间秒杀你!因为人家不是普通的,是RT的!
一个多线程的进程中,每个线程的调度的策略 如 fifo rr normal, 都可以不同。每一个的优先级都可以不一样。
实验举例, 创建2个线程,同时开2个:
运行2次,创建两个进程
sudo renice -n -5(nice -5级别) -g(global), 会明显看到 一个进程的CPU占用率是另一个的 3倍。
为什么cpu都已经达到200%,为什么系统不觉得卡呢?因为,我们的线程在未设置优先级时,是normal调度模式,且是 CPU消耗型 调度级别其实不高。
利用chrt工具,可以将进程 调整为 50 从normal的调度策略 升为RT (fifo)级别的调度策略,会出现:
chrt , nice renice 的调度策略 都是以线程为单位的,以上 设置的将进程下的所有线程进行设置nice值
线程是调度单位,进程不是,进程是资源封装单位!
两个同样死循环的normal优先级线程,其中一个nice值降低,该线程的CPU 利用率就会比另一个CPU的利用率高。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)