进程可以理解为是一个程序的执行过程,如果以进程为CPU调度单位的话,太消耗性能,因为每次调度都是以整个进程为单位,所以引入了线程的概念。
所谓的线程,其实是包含在进程中的,如果把进程想象成一个工厂的话,那么线程就是工厂中的一条条流水线,每一个线程都有一段自己要执行的命令逻辑,也就是每个线程都是一个独立的执行流。同一个进程中的线程共享着许多的资源,所以创建一个线程是比创建一个进程成本要低的,销毁也是。当前也有一些不能共享的资源,每个线程要独立的参与CPU的调度,所以每个线程的PCB后面四个属性都不是共享的
线程是依赖进程的,当我们创建一个进程之后,也会自动随之创建出一个主线程,进程是操作系统进行资源分配的最小单位,而线程则是操作系统进行调度和执行的最小单位,所谓的进程调度,实际上就是针对这个进程中的若干个线程进行调度。
同样的,线程也是需要管理的,并且和管理进程一摸一样。唯一有区别的就是PCB中还有一个线程组id,标志着这些线程是属于哪个进程的。
虽然以线程为单位进行调度效率会更快,但是并不是说一个进程的线程越多越好,当线程足够的时候,并发能力已经达到上限了,此时再增加线程的话,由于存在线程调度,所以会调度到那些没有用的线程上,反而会降低效率。
多线程编程:
有了多线程这个概念之后,我们自己写可执行文件的时候就可以进行并发编程,将某些代码执行单元独立为一个线程,提高我们的执行效率。
public class ThreadDemo1 {//多线程编程1
static class Mythread extends Thread {
@Override//需要重写run方法。
public void run() {
System.out.println(("helloworld"));
}
}
public static void main(String[] args) {
//首先通过内核提供的接口转化到Java的Thread类,创建出一个继承自Thread这个类的实例,这样我们可以在这个示例
//中自己定义指令/代码。然后通过向上转型,让父类Thread的引用去调用start方法来创建出一个线程。注意只有
//start之后线程(PCB)才被创建出来
//指定指令的方式有多种,此处用的是继承Thread然后自己定义指令
Thread t = new Mythread();
t.start();//这个方法执行之后就产生了一个PCB。这个PCB就会让CPU执行该线程对应的指令(也就是上面run方法中的逻辑)。
// 然后调用对应的硬件资源。
}//本质上这个程序进入内核后也是以多线程的方式并发执行的,但是此处我们在原有线程上自己新增了一个线程
//此处一共有两个线程,一个是main方法,这个程序启动后也就创建了一个进程,随之也就创建了一个主线程,也就是
//这个main方法,然后我们又自己新增了一个线程,这样的话main方法和run方法就是并发执行的
//可以借助第三方工具来查看该进程中的线程情况。JDK中内置了一个jconsole这样的程序
//如果直接运行这个jconsole我们是看不到的,因为每次执行完程序之后都会有Process finished这句话,就代表着此时我们
//执行的这个进程已经结束了。必须要想办法让进程不要结束得那么快,才能看得到
//可以在代码得最后面添加一个死循环。这样当变成机器码进入内核之后,会根据这个死循环代码来决定不去结束进程。
//此时由于机器码是一个死循环,所以我们得CPU就会一直去处理这个死循环机器码,所以此时我们创建的这个demo进程
//占据的CPU资源会很多
}
创建线程的方式有多种,但是本质上都是通过Thread这个类
- 通过显式继承Thread类的方式来实现
- 也可以通过匿名内部类的方式继承Thread类
- 显式创建一个类,实现Runnable接口,然后把这个Runnable的实例关联到Thread实例上
当要执行的任务越复杂,并发编程的效果就越明显。