进程(Process)
-
定义:进程是操作系统中资源分配的基本单位,每个进程都有自己独立的内存空间、文件描述符、全局变量等资源。
-
内存分配:进程有自己独立的地址空间。在一个进程内,任何内存的改变(如变量的更改)不会影响到其他进程。
-
资源开销:创建、销毁进程的开销较大,因为操作系统需要为进程分配和回收资源。
-
通信:进程间通信(IPC)比较复杂,常用的IPC机制包括管道、消息队列、共享内存、信号等。
-
执行方式:进程可以包含多个线程,进程的每个实例独立执行。
线程(Thread)
-
定义:线程是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源。
-
内存分配:同一进程中的线程共享地址空间、全局变量和文件描述符等资源。这意味着一个线程对某些变量的更改会影响到同一进程中的其他线程。
-
资源开销:创建、销毁线程的开销较小,因为线程之间共享进程的资源,不需要像进程那样分配独立的资源。
-
通信:线程之间的通信更简单,因为它们共享相同的内存空间,可以通过共享变量直接进行通信。
-
执行方式:线程是并发执行的,可以同时执行多个线程,以提高程序的并发性和性能。
举个现实中的例子:
工厂和生产线。
整个工厂就是一个进程。
假设它一个生产面包的工厂。
旁边还有一个生产衣服的工厂。
很显然它们两个工厂的生产资料不能共享。这就像两个进程的拥有两个独立的内存空间,且内存空间内变量不相互影响。
那这一个工厂里有四条生产线,生产线之间会相互影响,而且它们之间共享生产资料。这就像一个进程有四个线程,线程共享地址空间,而且线程之间可以相互影响。
那么讲完线程和进程,就也讲一讲并行和并发
-
并发(Concurrency):
- 指在同一时间段内多个任务交替执行。从宏观上看,多个任务似乎在同时进行,但实际上可能在微观上是快速切换的。
- 适用于单核和多核系统。
-
并行(Parallelism):
- 指在同一时刻多个任务真正同时进行。通常需要多核或多处理器系统,每个任务在不同的核心或处理器上运行。
- 只能在多核或多处理器系统上实现。
举个例子:
一个厨师在炒菜,但是他同时在用两个锅炒两个菜,那厨师只能一会用一个锅炒西红柿鸡蛋,一会用一个锅炒回锅肉。这样两个菜似乎在同时被炒,但这是一个厨师一段时间炒一个菜,另一段时间炒另一个菜来实现的,如此这样反复,达到同时炒两个菜的效果。
那现在有两个厨师了,一个厨师在用一个锅炒西红柿鸡蛋,另一个厨师在用一个锅炒回锅肉,这样就是真正的两个菜在同时被炒。
在上面这个例子中,厨师就像CPU,一个厨师在炒菜就是并发,CPU来回切换执行任务,但因为CPU切换速度很快,所以我们很难感觉到。而两个厨师就像是多核CPU,两个厨师在同时炒菜,就是CPU的两个核心在同时工作,这时任务真正的在被同时执行。
那接下来就结合代码来了解线程
JAVA中实现线程:
直接上代码
package Thread;
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
// 启动线程
t.start();
// 这里的代码还是运行在主线程中。
for(int i = 0; i < 15; i++){
System.out.println("主线程--->" + i);
}
}
}
class MyThread extends Thread {
@Override
public void run() {
// 编写程序,这段程序运行在分支线程中(分支栈)。
for(int i = 0; i < 15; i++){
System.out.println("分支线程--->" + i);
}
}
}
可以看看运行结果,这里可以看出,在调用t.start()方法后,两个线程在被同时运行。
那我改一行代码,再来看看运行结果
package Thread;
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
t.run();
// 这里的代码还是运行在主线程中。
for(int i = 0; i < 15; i++){
System.out.println("主线程--->" + i);
}
}
}
class MyThread extends Thread {
@Override
public void run() {
// 编写程序,这段程序运行在分支线程中(分支栈)。
for(int i = 0; i < 15; i++){
System.out.println("分支线程--->" + i);
}
}
}
这里我把t.start()改成了t.run()可以看到这里就是先执行了MyThread里的run里方法,再执行Main里的方法。
这里面run只是被当做成一个方法被调用,而start才会创建一个新线程一起运行。
在JAVA中实现线程的两种方法
1.直接继承java.lang.Thread,重写run方法
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
// 启动线程
t.start();
// 这里的代码还是运行在主线程中。
for(int i = 0; i < 100; i++){
System.out.println("主线程--->" + i);
}
}
}
class MyThread extends Thread {
@Override
public void run() {
// 编写程序,这段程序运行在分支线程中(分支栈)。
for(int i = 0; i < 100; i++){
System.out.println("分支线程--->" + i);
}
}
}
2.实现java.lang.Runnable接口,实现run方法。
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
for(int i = 0; i < 100; i++){
System.out.println("主线程--->" + i);
}
}
}
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("分支线程--->" + i);
}
}
}
下面是有关线程的一些方法:
方法类型 | 方法名 | 效果 |
---|---|---|
Thread | .currentThread() | 获取当前线程对象 |
String | .getName() | 获取线程对象名字 |
void | .setName() | 修改线程对象名字 |
void | .sleep() | 让线程休眠 |
void | .interrupt() | 终止线程的睡眠 |
int | .getPriority() | 获取线程优先级 |
void | .setPriority() | 设置线程优先级 |
void | .join() | 将一个新线程合并到当前线程中 |
void | .wait() | 让活动在当前对象的线程无限等待 |
void | .notify() | 唤醒当前对象正在等待的线程 |
void | .notifyall() | 唤醒当前对象全部正在等待的线程 |