一.进程与线程
进程
我们在进行操作电脑的时候,通常会打开浏览器,通讯工具等应用程序,这个时候CPU通过作业调度在内存中就会分配一些空间让它们处于宏观上的运行状态(处于可以被CPU执行的状态),而这部分空间资源就可以说是相应的进程占据的,很显然运行不同的程序需要不同的进程,在内存中也为它们分配独立,无共享的区域。静态描述进程的是PCB快(数据结构集),它是系统感知进程存在的唯一实体,通常包含进程名(或者标识符),用户名(或者用户标识号),家族关系。
进程的状态
就绪态:进程获得了除cpu以外的其他资源,进入抢占cpu的队列中
执行态:cpu正在执行该进程
阻塞态:进程继续执行的某个事件条件(如I/O设备)尚未具备
挂起态:处理机调度将进程调离内存进入外存
终止态:进程结束。(当我们撤销一个进程的时候,会通过PCB结构递归删除所有它的子进程,否者子进程可能处于无法控制的状态。)
(撤销进程的流程)
进程阻塞与挂起
阻塞详细流程图:
阻塞与挂起的区别:
1.首先阻塞是被动行为,当资源完备后,任然受cpu调度进入就绪状态,而挂起是主动行为,进程外存后,不受cpu作业调度,通过resume()函数进入就绪态。
2.发生原因不同:阻塞原因是针对这单个进程等待发生的条件不具备,而挂起原因内存资源有限暂时调离内存的过程。
线程
线程也称为轻型进程。线程的作用域在单个特定的线程内。一个进程内支持多个线程。这些线程的特点是:共享一定的内存区和数据。它们是彼此相互独立的,可以并发进行,宏观上看来是同时运行的,但实际上CPU在一个时刻只能处理一个子任务,分时操作系统采用了“时间片的策略”,即CPU将时间切割为时间片,然后将这些时间片分配给这些程序,只有获得时间片的程序才被执行。还有一个概念叫做主线程,它是进程初始化后自动启动的线程,如java里面的main。
进程与线程的区别
资源方面:一个进程包含多个线程,同类的多个线程共享一块内存和数据资源,而进程之间(包括子进程与父进程之间)是完全独立的,不存在资源的共享。
功能方面:进程是并发程序执行过程中资源分配的基本单元,线程是程序运行与调度的基本单元。
处理机的调度
(图片来自:http://www.cnblogs.com/dyllove98/p/3249243.html)
cpu调度发生的情况总结:
(1)创建进程:当创建新进程时,要决定运行父进程还是子进程;
(2)进程终止:每当一个进程终止时,必须进行调度;从就绪队列中选择要执行的下一个进程;
(3)等待事件:运行进程由于等待I/O、信号量或其他原因而不得不放弃CPU,这样就必须选择另一个进程投入运行。
(4)中断发生:当I/O设备完成时后会发出I/O中断,原先等待I/O的进程就从阻塞态转换为就绪态。调度程序要决定是调用就绪队列的进程,还是选择被打断的进程。
(5)运到到时:当进程进程分配的时间片用完,此时选择新的进程投入运行。
(很明显挂起的进程不需要cpu进行作业调度)
二.进程的优先级
对于所有线程都具有它自己的优先级,优先级高的首先被CPU执行,当某个线程处于运行状态时,那些尚未拥有优先级的处于可运行状态的线程会自动获得优先级。还存在另一种情况,当某个线程在运行时,一个更高优先级的线程处于可运行状态,这是这个线程被立即执行,这称为先占式调度。
三.线程的开发方法
1.实现Runnable接口实现它的run()方法
2.继承Thread类,覆盖它的run()方法----Thread已经实现了Runnable接口实现了run方法,因此继承Thread的类是覆盖run()方法
(Runnable实现线程解决了java单一继承的局限性)
四.线程池
背景:例如web服务器,数据库服务器,文件服务器等服务器端应用程序,都面临这来着客户端的请求,这些请求的整体特点是:单个任务处理的时间很短,但是请求的数目巨大
瓶颈:对于在进程内划分线程已经是个很大的改进,但仍然存在以下问题,对于每个到达服务器端的请求,单独为它们创建一个线程,将请求处理完后,再将线程销毁,然后线程创建与销毁的时间比处理请求的时间要多得多。同时线程过多,也会消耗内存。
改进:初始的时候便创建一个线程池(拥有i个线程),当请求到达时便拥有线程池中的某个线程(免去了创建时间),当线程池中的所有线程都被占据的时候,其他请求需要等待,占据线程的请求处理完后,线程又空余出来,新的请求可以进入。此处会有一定的等待时间,但和原来繁琐的线程创建和销毁相比,性能改进不少,同时线程池的构建也需要不断优化。
五.线程的状态以及运行控制
线程的运行状态
上述表明了线程在运行过程中的5种状态:
初始化状态:通过new实例化了一个线程对象,但该对象是空的并没有分配资源
可运行状态:通过start()方法,为该线程分配了除了CPU外的所需资源(进入队列,排队等待CPU),处于Runnable状态
运行状态:线程调度控制CPU处理一个Runnable线程,这个时候才实际上执行run()函数的代码
阻塞状态:正在运行的线程由于某些事件尚未具备(例如:输入输出)从而进入进入阻塞状态,来到阻塞池等待
挂起状态: A,通过调用sleep()方法使线程进入休眠状态,线程在指定时间内不会运行。
B,通过调用join()方法使线程挂起,如果某个线程在另一个线程t上调用t.join(),这个线程将被挂起,直到线程t执行完毕为止。
C,通过调用wait()方法使线程挂起,直到线程得到了notify()和notifyAll()消息,线程才会进入“可执行”状态。
(区别进程的挂起,线程不拥有资源,也不存在将资源放入外存中)
死亡状态:线程结束了(Dead),可由这些原因导致:1.该线程运行完毕。2.意外退出。3.父类线程控制其消亡。
线程的运行控制
1.线程的启动start()
public class test extends Thread{ public void run(){ while(true){ System.out.println("子线程运行中---"); } } } public static void main(String args[]){ test t =new test(); t.start(); try{ Thread.sleep(2000);//父线程进入等待池等待 } catch(Exception e ){ } t.stop(); }//在上述实例中,通过main主线程创建了一个子线程,他们是相互独立,并发执行,但是父线程可以控制子线程的状态,例如stop()
2.休眠(sleep)与挂起(yield),join
相同:休眠与挂起都会使线程暂停处于等待状态
区别:休眠可以设定时间,并且期间任何优先级的线程都可以由可运行状态变为可执行状态,而挂起不可以设定时间,只有同等优先级的线程可以变成可执行状态
join立即执行方法
class test1 extends Thread{ static int count = 0; public void run(){ for(int i=0;i<5;i++){ count++; } } } public class join { public static void main(String ats[]){ test1 t = new test1() ; try { t.start(); t.join();//如果没有这条语句输出为0,当前输出为5 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(test1.count); } }
3.wait()与sleep()区别
public class waitandsleep extends Thread{ static int number = 10; public void firstMethod() throws Exception { synchronized (this) { number += 100; System.out.println(number); } } //下面是否加上static,又是一种同步锁,结果不一样 public synchronized void secondMethod() throws Exception { //用于区别wait和sleep //sleep(在Thread的静态方法)的时候仍然占有机锁,释放cpu // Thread.sleep(2000);//当把改行注释转移到this.wait(2000)时,运行输出2100 //wait(Object的方法)的时候释放机锁,其他线程块可以进入,释放cpu this.wait(2000);//输出110 number *= 200; } public void run() { try { firstMethod(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { waitandsleep ws=new waitandsleep(); ws.start(); ws.secondMethod();
ps:纯手打,图片是拍的书上的,勉强看的清楚,感觉自己写博文一直有个弊端,试图在一篇文章里面囊括一个大的知识工厂,但现在发现能将一个点讲清楚就非常非常困难,所以这篇失败的博文,还将继续改善,fighting!!