一、程序:包含指令和数据的文件,被存储在磁盘或其他数据存储设备中,是一段静态代码。
二、进程:
1、系统正在运行程序的一种抽象,是系统运行程序的基本单位,拥有系统赋予的独立的内存地址空间。
2、进程是动态的,系统运行一个程序即是一个进程从创建、运行到消亡的过程。
3、每个进程都会占有某些系统资源,如CPU时间、内存空间、文件、输入输出设备的使用权等。所以进程也是系统资源进行分配和调配的最小单位。
简单的说,一个进程就是一个执行中的程序,例如QQ、浏览器。
三、线程:
1、进程的基本执行单元,一个进程的所有任务都在线程中执行。
2.一个进程最少有一个线程,进程中所有线程都共享进程所在的内存空间以及其所拥有的系统资源。
3.每个线程都运行在同一进程的上下文中,共享同样的代码和全局数据。
简单的说,打开一个程序,使用它的某个功能就是一个线程,例如QQ添加好友功能。
四、进程与线程之间的联系与区别:
1、线程分布在进程中,是进程划分的更小的运行单位。两者最大的不同在于各进程之间相互独立,互不干扰,而各线程之间则不一定,因为同一进程中的线程共享进程下面的资源,可以互相通信并影响。
2、进程属于操作系统的范畴,在同一时间段可以运行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。
五、并发:在同一时刻只能执行一条指令,但多个进程指令被快速的轮换执行,这在宏观上看是多个进程同时运行,但在微观上并不是同时运行,而是将时间分成若干段,使多个进程快速交替的执行。
并行:在同一个时刻,有多条指令在多个处理器上执行。
六、并发与并行的区别:
1、并行存在多处理器系统中,而并发在单处理器和多处理器系统中都可存在,这是因为并行要求程序同时执行多个操作,而并发要求程序假装执行多个操作,即一个时间段只执行一个操作,多个操作快速轮换执行。
2、当有多个线程在操作,而系统只有一个处理器时,系统会把CPU运行时间分成若干个时间段,从而将它们分配给各个线程执行。在一个时间段里,只有一个线程在运行,其它线程处于挂起状态,这种方式称为并发。
3、当有多个线程在操作,而系统有多个处理器时,线程的操作可能非并发。因为一个CPU在执行某个线程时,另一个CPU在执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,我们称之为并行。
七、线程的生命周期:
新建(New):当使用new关键词创建一个线程时,该线程就处于新建状态。此时的线程对象并未表现出任何线程的动态特征,程序也不会执行线程的线程执行体。
就绪(Runnable):调用start()方法后,线程并没立即运行,而是进入就绪状态,做好准备,一旦被分配到CPU执行权利,就会立马执行任务。
运行(Running):处于就绪状态下的线程获得CPU,就会执行run()方法,进入运行状态。一个线程开始运行后,不可能一直占有CPU,它在运行过程中会被中断,重新进入就绪状态,目的就是给其它线程获得CPU并运行的机会。对于采用抢占式调度策略的操作系统而言,它会给每个可执行线程分配一个时间段来处理任务,当该时间结束时,系统就会剥夺该线程所占有的资源,让其它线程获得执行的机会,此时失去CPU执行权力的线程就无法运行,进入就绪状态,等待系统分配资源。对于采用协作式调度策略的小型设备(如手机)而言,只有线程调用yield()方法主动放弃CPU执行权力,其它处于就绪状态下的线程才有可能获得执行的机会。
阻塞(Blocked):处于运行状态下的线程,若出现以下情况,就会进入阻塞状态
1、线程调用了sleep()方法,主动放弃占有的CPU;
2、线程调用了一个阻塞式IO方法,在该方法返回前,该线程被阻塞;
3、线程试图获得一个同步监视器,而该同步监视器被其它线程所占有;
4、线程在等待某个通知;
5、线程调用了suspend()方法,该线程被挂起。
处于阻塞状态下的线程,当阻塞解除后,并不能立即获得CPU,而是进入就绪状态,等待线程调度器再次调度它。以下情况可以让被阻塞的线程进入就绪状态
1、调用的sleep()方法达到指定的时间;
2、调用阻塞式IO方法已返回;
3、线程成功获得试图取得的同步监视器;
4、线程在等待通知时,其它线程发布通知;
5、被挂起的线程调用了resume()方法。
死亡(Dead):线程的run()方法执行完成,或者线程捕获到异常,或者线程调用了stop()方法,该线程就会结束,结束的线程就进入了死亡状态。这里需要注意的一点就是线程一旦结束,它所涉及的数据和内存都会被释放,不能再次被调用,使用start()方法就会报错。
线程的创建方法(MyThread的创建)
(一)j继承Thread类
public class MyThread extends Thread{
public void run() {
System.out.println("当前线程为:"+Thread.currentThread().getName());//获取当前线程名称
//若该方法中涉及循环 需要调用sleep()方法时 应用
/*try {
MyThread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
public static void main(String[]args) {
MyThread t=new MyThread();
t.start();//不能直接使用run()方法 而应用start()方法使线程进入就绪状态
}
}
(二)实现Runnable接口
public class MyThread implements Runnable{
public void run() {
System.out.println("当前线程为:"+Thread.currentThread().getName());//获取当前线程名称
//若该方法中涉及循环 需要调用sleep()方法时 应用
/*try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
public static void main(String[]args) {
MyThread runnable=new MyThread();//创建一个runnable对象
Thread t=new Thread(runnable);//将runnable对象传给Thread类 创建一个线程对象
t.start();//不能直接使用run()方法 而应用start()方法使线程进入就绪状态
//注意:不能直接用runnable对象调用start()方法 因为该方法只有Thread类有
}
}
一般我们倾向于用实现Runnable接口来新建线程,原因是:
1.只能继承一个类,但可实现多个接口
2.接口使用起来更灵活