上节课内容回顾
进程, 进程调度, PCB结构,进程的虚拟空间地址,进程间的通信
进程和线程之间的区别和联系:
- 进程包含线程!一个进程里面可以有一个线程也可以有多个线程
- 进程和线程都能解决并发编程问题的场景,但是进程在频繁创建和销毁中,开销更高,线程开销更低(线程比进程更轻量级)
- 进程是系统分配资源(内存,文件资源…)基本单位,线程是系统调度执行的基本单位(CPU)
- 进程之间是相互独立的,各自有各自的虚拟空间地址.同一个进程的内部的多个线程之间共用一个内存空间以及文件资源.一个进程挂了其他进程一般都没事,但是一个线程挂了可能把整个进程搞崩
多线程仍然是最主流最常见的一种并发编程的方式
Java中如何进行多线程编程;
在Java标准库中,就提供了一个Thread类来表示操作线程
Thread可以视为Java标准库提供的API
创建好的Thread实例,其实和操作系统的线程是一一对应的关系,操作系统提供了一组关于线程的API(C语言风格)Java对于这组API进一步封装,就成了Thread类
通过Thread类创建线程,写法有多种,其中最简单的就是创建子类继承Thread,并且重写run方法
class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello thread!");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
}
}
run
描述了每个线程内部要执行那些代码,每个线程都是并发执行的(各自执行各自的代码),因此就需要告诉这个线程你要执行什么代码
start
需要调用这个start方法,才是真正的在系统上创建了线程,才是真正的执行上面的run操作,在调佣出start方法之前,系统是没有创建出线程的
线程之间是并发执行的,如果一个循环中不加任何的限制,这个循环转的速度非常快,导致打印的东西太多了,根本看不过来,就可以加上一个sleep操作,来强制让这个线程休眠一段时间
在一个进程中至少有一个线程,在一个Java进程中也是至少有一个调用main方法的线程,自己创建的线程和自动创建的main线程就是并发执行的关系(宏观看起来是同时执行)
使用场景:
class MyThread extends Thread {
@Override
public void run() {
while(true) {
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
while(true) {
System.out.println("hello main!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
现在两个线程,都是先打印一条就休眠1s,当1s时间到了之后,系统先唤醒谁那?(看起来这个顺序不是完全确定的)随机的,对于操作系统来说,内部对于线程之间的调度顺序,在宏观上认为是随机的(抢占式的)
创建一个类,实现Runable接口,再创建Runable实例传给Thread实例
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("hello thread!");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
就是上面俩种写法的翻版,使用了匿名内部类
public class Main {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("hello thread!");
}
};
thread.start();
}
}
创建了一个匿名内部类,继承自Thread类,同时重写run方法,同时new这个匿名内部类的实例
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello thread!");
}
});
thread.start();
}
}
new Runable针对这个创建的匿名内部类,同时new出的Runable实例传给Thread 的构造方法
通常认为Runable这种写法更好一点,能够能够做到让线程和线程执行的任务更好的解耦,写代码一般希望高内聚,低耦合
相当于第四种写法的延伸,使用lambda表达式,是使用了lambda代替了Runable而已
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("hello thread!");
});
thread.start();
}
}
以上五种写法都很常见大家都应该掌握