进程
当我们双击运行电脑程序的时候,操作系统就会为其创建一个进程,这个进程就是来维护这个程序在电脑内存上运行的状态(从双击运行到点击关闭期间)线程是操作系统分配资源的基本单位
为什么要有进程
进程的出现就是因为我们的单核CPU发展到了瓶颈了,这时就出现了多核CPU,而进程也是为了更加充分的利用多核CPU的资源**(并行+并发)**,但是每个进程的创建与销毁,消耗了太多的资源,所以就以进程为基础,剥离出来了线程的概念
并行与并发
并行:一个CPU以时间片轮转的方式依次执行每个线程,某一段时间宏观来看,就像是多个线程一同执行一样
并发:多个线程在同一个时间点同时运行
线程
线程是从进程中剥离出来的,因此,一个进程是可以剥离出多个线程的,而进程是操作系统分配资源的基本单位,所以这多个线程就会公用该进程的资源,因此线程的创建与销毁是比进程的消耗更小了,从而提升了并发编程的效率
虽然线程相对于进程的消耗已经减少了许多,可是在有的场景下,就是需要频繁创建与销毁线程,这时线程的消耗也起来了,所以 Java 进入了 线程池的概念
线程是操作系统随机调度的基本单位
主要应用场景
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O 密集型,多线程可以充分利用CPU,在执行IO操作的时候(需要等待),让线程去干点别的事情
- 进程包含线程,线程是在进程内部的
- 每个进程都有自己独立的虚拟地址空间,也有自己独立的文件描述符表;同一个进程的多个线程之间,则共用这一份虚拟地址空间和文件描述符表
- 进程是操作系统中资源分配的基本单位。线程是操作系统中调度执行的基本单位
- 多个进程同时运行时,如果一个进程挂了,一般不会影响别的进程;而同一个进程里面的多个线程之间,如果一个线程挂了,很可能把整个进程带走了,当前进程的其他线程也就没了
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("Hello thread");
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
while (true) {
System.out.println("Hello main");
}
}
// 直接匿名内部类也可以
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("hello thread");
}
};
thread.start();
System.out.println("hello main");
}
}
class MyRunable implements Runnable {
@Override
public void run() {
System.out.println("hello thread");
}
}
public class Demo2 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunable());
thread.start();
System.out.println("hello main");
}
}
public static void main(String[] args) {
Thread thread = new Thread(()->{
System.out.println("hello thread");
});
thread.start();
System.out.println("hello main");
}
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否有后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
️说明:
ID:是线程的唯一标识,多个线程不能重复 (这里能获取的就是JVM中的ID标识,而操作系统内部也有一个ID)
名称:是线程的名称(方便程序员调试的时候查看)
状态:表示线程所处的情况(JVM中的状态,一共6种,往下看)
优先级:理论来说,优先级高的线程优先被调度到(它是一个数值来表示的,数值越小,优先级越高)
后台线程:JVM会在一个进程的所有非后台线程结束后,才会结束运行
是否存活:简单理解为run方法是否运行结束
线程中断:是否要提前截至 run 方法
两种中断机制:1. 自己定义一个 flag 标志位,来控制;2. Thread提供的一个静态方法
// 自定义标志位来控制线程是否结束
public class Demo7 {
// 用一个布尔变量表示线程是否要结束
private static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!isQuit) {
System.out.println("线程运行中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程执行结束");
});
t.start();
// 5秒钟后,中断线程
Thread.sleep(5000);
isQuit = true;
}
}
调用 interrupt() 来实现中断,会产生两种情况:
- 若当前线程处于非堵塞状态,那么程序就会修改内置的标志位
- 若当前线程处于堵塞状态,inerrupt()的调用就会让线程中的sleep抛异常,然后被catch捕获,然后我们自己决定退不退出
public class Demo8 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
// Thread.currentThread() 获取当前线程的引用
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// e.printStackTrace();
// [1] 立即退出
// break;
// [2] 稍后退出
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// ex.printStackTrace();
// 处理退出前的任务
break;
}
}
}
System.out.println("线程结束");
});
t.start();
// 调用 interrupt() 会产生两种效果:
// 1. 若当前线程处于非堵塞状态,那么程序就会修改内置的标志位
// 2. 若当前线程处于堵塞状态,interrupt()的调用就会让线程中的sleep抛异常,然后被catch捕获,然后我们自己决定退不退出
t.interrupt();
}
}
- NEW:线程创建好了,但是还未执行 start 方法,也就是还没把该线程加到 PCB 队列中,参与调度
- TERMINATED:run 方法体执行完毕,但是程序还没结束(thread 变量还未销毁)
- RUNNABLE:调用了 start 方法后的状态,可能在 CPU上运行,也可能在就绪队列中等待调度上CPU
- BLOCKED:当前线程在等待锁,导致阻塞
- WAITING:当前线程在等待唤醒,导致阻塞(wait 操作)
- TIMED_WARNING:当前线程在一定时间内阻塞(sleep,join操作)