进程是什么?
在计算机中,进程代表了内存中正在运行的应用程序,计算机中的资源(cpu 内存 磁盘 网络等),会按照需求分配给每个进程,从而这个进程对应的应用程序就可以使用这些资源了。
线程是什么?
线程是进程中的一个代码执行单元,负责当前进程中代码程序的执行,一个进程中有一个或多个线程。当一个进程中启动了多个线程去分别执行代码的时候,这个程序就是多线程程序。
并发与并行
并发:
并行:
抢占式调度
JVM中的线程,使用的为抢占式调度。
常见的调度方式:
- 时间片轮转
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 - 抢占式调度
系统会让优先级高的线程优先使用 CPU(提高抢占到的概率),但是如果线程的优先级相同,那么会随机选择一个线程获取当前CPU的时间片。
首发main线程如何去做的?
- 当前我们使用java命令去运行一个类的时候,会先启动JVM,这个JVM对于计算机来讲,就是一个应用程序,所以同时系统中也会启动一个进程和这个JVM对应。
- 使用 java 命令来运行一个类的时候,首先会启动JVM(进程),JVM会在创建一个名字叫做main的线程,来执行类中的程序入口(main方法)
public class Test {
public static void main(String[] args) {
//获取执行当前方法的线程对象
Thread currentThread = Thread.currentThread();
System.out.println("执行当前方法的线程名字为:"+currentThread.getName());
}
}
//运行结果:
执行当前方法的线程名字为:main
-
使用java命令运行Test类,会先启动JVM
-
应用类加载器通过CLASSPATH环境变量配置的路径,找到Test.class文件,并加载到方法区。
注意,这里会同时生产一个Class类型对象,来代表这个Test类型,并且会优先处理类中的静态代
码(静态属性、静态方法、静态代码块) -
JVM创建并启动一个名字叫做main的线程
-
main线程将Test中的main方法加载到栈区中
-
在栈里面,main线程就可以一行行的执行方法中的代码了
-
如果在执行代码中,遇到了方法调用,那么线程会继续把被调用的方法,加载到栈中(压栈操
作),然后执行栈顶这个最新添加进来的方法,栈顶方法执行完,就释放(出栈操作),然后在
进行执行当前最新的栈顶方法(之前我们画过栈里面的方法调用图,例如在异常的学习过程中) -
代码执行过程输出执行结果
-
当前是单线程程序,main线程结束了,JVM就停止了,如果是多线程程序,那么JVM要等所有线程
都结束了才会停止
其他的线程是如何去创建的
Java中通过继承Thread类来创建并启动一个新的线程的步骤如下:
-
定义 Thread 类的子类(可以是匿名内部类),并重写 Thread 类中的 run 方法, run 方法中的代码就是线程的执行任务
-
创建 Thread 子类的对象,这个对象就代表了一个要独立运行的新线程
-
调用线程对象的 start 方法来启动该线程
直接匿名内部类创建
public class Test {
public static void main(String[] args) {
Thread t = new MyThread(){
@Override
public void run() {
System.out.println("端茶")
}
};
t.start();
}
}
首先我们要知道我们创建的线程与main线程的区别
我想以上俩张图可以说明,创建新线程是创建一个新得栈区,而之前的是只在一个main线程上使用
- 注意1,之前所提到的栈区,又被称为方法调用栈,是线程专门执行方法中代码的地方,并且每一个线程,都有自己独立的栈空间,和别的线程相互不影响
- 注意2,最先启动的线程是主线程(main线程),因为它要执行程序的入口main方法,在主线程中,创建并且启动了t线程,启动之后main线程和t线程将各自独立运行,并且争夺CPU的时间片
- 注意3,线程启动之后(调用start方法),会开始争夺CPU的时间片,然后自动执行run方法,如果子类对象重写了,那么就调用到重写后的run方法
Runnable接口:线程代码入口
Thread 类也是 Runnable 接口的实现类
该接口中只有一个抽象方法 run
public class Test {
public static void main(String[] args) {
//Runnable接口的实现类中,重写了run方法,指定线程的执行任务
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("端茶")
}
};
//创建线程对象,指定执行任务
Thread t = new Thread(run);
t.start();
}
}
意义:
初看和直接创建Thread没什么区别都是重写run方法调用start方法去运行,可是直接创建Thread方法的话就一个线程只能完成一个任务,而使用Runnable的话当你需要什么方法就传给线程Runnable创建的run方法,使得代码更加的灵活,耦合度降低。
线程名字的获取
创建出来的线程会有一个默认的名字是由Thread的构造器初始化的
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
自定义名字
Thread t = new Thread("t线程");
或者
Thread t = new Thread();
t.setName("t线程");
又或者
Thread t = new Thread(new Runnable(){
public void run(){
//执行任务
}
},"t线程");
查看名字
Thread.currentThread().getName();
获取当前线程的名字的方法
public class Test {
public static void main(String[] args) {
String name = Thread.currentThread().getName();
System.out.println("执行当前main方法的线程是:"+name);
Runnable run = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("执行当前run方法的线程是:"+name);
}
};
Thread t = new Thread(run);
t.start();
}
}
//运行结果为:
执行当前main方法的线程是:main
执行当前run方法的线程是:Thread-0
多线程的优点
1.资源利用率更好
2.程序设计在某些情况下更简单
3.程序响应更快