什么是进程?和程序一样吗?
程序是一系列有组织的文件,封装操作系统的各种API,实现不同的效果和进程
比如QQ这个软件的核心就是聊天,这就封装了发送网络请求、接收网络请求等一系列的API,最后将其变成文件
进程是程序在系统中的一次执行过程
进程是现代操作系统中资源分配(CPU、内存等关键系统资源)的最小单位
之前使用MySQL服务,其实就是启动MySQL的服务端进程
IP地址是在网络上唯一标识一台主机的位置,就相当于我们所在地址,别人通过这个地址就能找到这个地方
通过IP地址能找到主机,那如何定位主机上的某个进程呢?
我们通过端口号在操作系统上唯一地定位一个进程
Http的端口号:80,Https的端口号:443,还有我们写的MySQL服务端启动之后的进程端口号就是3306
进程的端口号一般不变,进程的PCB(操作系统描述本次进程的信息接口)的PID每次启动进程都会改变
线程的概念我们现代OS都是多线程操作系统,那究竟什么是线程
比如买CPU的时候都有几核心几线程
4核8线程指的是当前电脑的CPU有4个核心(4个独立的芯片),每个核心能并行处理两个线程,所以就能并行执行8个线程
线程是进程中的一个独立任务,或者说是一个子类
在浏览器上可以一边下载视频,一边听音乐,下载视频就是一个线程,听音乐就是另一个线程
不同的进程之间是相互独立的,而同一个进程的所有线程是共享进程的资源的,线程是操作系统任务执行的基本单位
线程和进程的区别- 进程是OS资源分配的基本单位,线程是操作系统中任务执行(也称系统调度)的基本单位
- 创建和销毁进程的开销要远比创建和销毁线程大得多(创建和销毁一个进程的时间要比创建和销毁一个线程大得多),线程更加轻量化
- 调度一个线程比调度一个进程快得多,比如你启动浏览器和新建一个浏览器的标签速度是由明显差别的
- 进程包含线程,每个进程至少包含一个线程(主线程)
- 进程之间彼此相对独立,不同的进程不会共享内存空间,而同一个进程的线程共享内存空间
我们在程序中写的main方法就是主线程,java命令 主类名称就是在启动一个Java进程
JDK提供的线程库实际上就是利用操作系统提供的线程库进行二次封装
多线程的使用以前写代码的入口都是main主方法(主线程),所有的调用都是从主方法开始的,所有的任务都在主方法(主线程)中进行
Java中描述线程这个对象的类是 - java.lang.Thread,该类线程的核心类,都是通过Thread类来启动一个新的类
多线程代码示例:
public class FirstThreadDemo {
private static class MyThread extends Thread {
//run方法就是线程的核心工作方法,线程要干的所有事情都在run方法中进行定义
@Override
public void run() {
Random random = new Random();
while (true) {
//打印当前线程的名称
System.out.println(Thread.currentThread().getName());
//当前线程随机暂停0-9秒
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
// main方法就对应一个主线程
public static void main(String[] args) {
//创建三个线程对象
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
//启动三个子线程
m1.start();
m2.start();
m3.start();
Random random = new Random();
while (true) {
//打印当前线程的名称
System.out.println(Thread.currentThread().getName());
//当前线程随机暂停0-9秒
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
启用线程就调用start方法,线程启动之后会由JVM自动执行每个线程的run方法,每个线程的执行都是并行的
只有主线程的时候,代码的执行顺序就是从主方法第一行,而上面代码中的多了3个子线程,则4个线程就在电脑中同时执行,互不干扰
代码执行起来的结果:
主线程和子线程同时执行的图示:
问题:根据上面代码如果我们让所有线程顺序执行,那么主方法会不会打印输出?其他线程会不会打印输出?
如果是顺序执行,主方法肯定不会打印输出,要等子线程结束之后才能执行后面的代码,但是子线程中是一个死循环,所以除了第一个子线程,其他子线程都不会打印输出
run方法和start方法的区别
run方法是线程类的工作任务 - run方法决定了线程启动之后要干的事情,当run方法执行完毕(run由JVM来执行),线程就进入销毁状态
start方法就Thread类中启动线程的方法,只有当线程对象调用start方法之后才会被系统调度,进入运行状态
如何查看JVM中的线程状态
前两种创建线程的方式JDK和JRE相比最大的区别,提供了很多开发程序会用到的辅助工具
我们在终端执行jconsole命令 - 查看当前运行JVM内部的线程情况
Java中创建一个线程一共有四种方法:
- 继承Thread类,覆写run方法(线程的核心工作任务)
- 实现Runnable接口,覆写run方法
- 实现Callable接口,覆写call方法
- 使用线程池创建线程
第一二种方法,最终启动线程都是通过Thread类的start方法启动线程,至于第三四种方法,不在此模块讲解,会在后面的内容进行介绍
1. 继承Thread类该方式下的线程创建与启动三步走:
- 一个子类继承Thread类
- 覆写run方法
- 产生当前子类对象,然后调用start方法启动子线程
代码示例:
// ThreadMethod这个子类就是一个线程实体
public class ThreadMethod extends Thread{
@Override
public void run() {
System.out.println("这是子线程的的输出结果");;
}
}
public class Main {
public static void main(String[] args) {
// 创建线程类的对象
ThreadMethod mt = new ThreadMethod();
// 启动子线程
mt.start();
//执行主线程的语句
System.out.println("主线程的输出结果");
}
}
输出结果:
调用start方法启动线程,是由JVM来产生操作系统的线程并启动的,到底什么时候会真正启动,对我们来说是不可见的,也不能控制,因此会出现上面两种情况
2. 实现Runnable接口该方式的线程创建依然是三步走:
- 实现Runnable接口
- 覆写run方法
- 和第一种方法一样,还是创建Thrad类的对象,调用start方法
代码示例:
public class RunnableMethod implements Runnable {
@Override
public void run() {
System.out.println("这是Runnable方式实现的子线程任务");
}
}
public class Main {
public static void main(String[] args) {
// 创建线程任务的对象
RunnableMethod runnableMethod = new RunnableMethod();
// 创建线程对象,将任务对象作为参数传入线程对象的构造方法中
Thread thread = new Thread(runnableMethod);
// 启动线程
thread.start();
// 执行主线程的语句
System.out.println("主线程的输出结果");
}
}
相比第一种,更推荐使用方式2,实现Runnable接口更加灵活,子类还能实现别的接口,继承别的类
而使用方式1的话,就只能继承Thread,存在单继承局限性
3. 关于方式1和方式2的不同写法1.使用匿名内部类创建Thread对象
public static void main(String[] args) {
// 匿名内部类继承Thread类
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("匿名内部类继承Thread类");
System.out.println("这是子线程" + Thread.currentThread().getName());
}
};
t1.start();
System.out.println("这是主线程" + Thread.currentThread().getName());
}
// 并发执行的结果
匿名内部类继承Thread类
这是主线程main
这是子线程Thread-0
2.使用匿名内部类实现Runnable接口
public static void main(String[] args) {
// 这里即实现了Runnable接口,又进行了传参
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类实现Runnable接口");
System.out.println("这是子线程" + Thread.currentThread().getName());
}
});
thread.start();
System.out.println("这是主线程" + Thread.currentThread().getName());
}
// 并发执行的结果
匿名内部类实现Runnable接口
这是子线程Thread-0
这是主线程main
3.使用Lambda表达式
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("Lambda表达式实现Runnable接口"));
t1.start();
System.out.println("这是主线程" + Thread.currentThread().getName());
}
// 并发执行的结果
这是主线程main
Lambda表达式实现Runnable接口
由于Lambda表达式只能用于函数式接口的实现,所以Thread类就享受不到这种简化写法了
暑期编程PK赛 得CSDN机械键盘等精美礼品!


