目录
一、认识线程(Thread)
1.概念
(1)线程是什么?
线程是程序执行流的最小单元,是进程中的一个实体,是被系统独立调度和分派的基本单位。⼀个线程就是⼀个"执⾏流"。每 个线程之间都可以按照顺序执⾏⾃⼰的代码,多个线程之间"同时"执⾏着多份代码。
举个例子:
一家餐厅要准备一场大型宴会的餐食,既需要烹饪热菜,又要制作精致的甜品,还得准备新鲜的沙拉。
如果只有主厨一人操刀,不仅任务繁重,而且会耗费大量时间,难以按时完成宴会餐食的准备。为了高效完成任务,主厨邀请了两位帮厨小刘和小赵前来协助。三人分别负责不同的工作:主厨负责烹饪热菜,小刘专注于制作甜品,小赵着手准备沙拉。他们各自在厨房的不同区域同时开工,就像拥有了三个独立的 “执行流”,共同为这场宴会餐食的准备努力,本质上都是在完成同一场宴会的餐饮任务。
此时,这种情况就好比多线程,将准备宴会餐食这个大任务拆解成不同的小任务,交给不同的 “执行流” 并行执行。因为是主厨组织并统筹整个任务,所以主厨就相当于主线程(MainThread)
(2)为啥要有线程?
⾸先,“并发编程"成为"刚需”。
- 单核 CPU 的发展遇到了瓶颈。要想提高算力,就需要多核 CPU。而并发编程能更充分利用多核 CPU 资源。
- 有些任务场景需要 “等待 IO”,为了让等待 IO 的时间能够去做一些其他的工作,也需要用到并发编程。
其次,虽然多进程也能实现并发编程,但是线程比进程更轻量。
- 创建线程比创建进程更快。
- 销毁线程比销毁进程更快。
- 调度线程比调度进程更快。
线程虽然⽐进程轻量,但是⼈们还不满⾜,于是⼜有了"线程池(ThreadPool)和"协程"
(3)进程和线程的区别
- 进程是包含线程的。每个进程至少有一个线程存在,即主线程。
- 进程和进程之间不共享内存空间。同一个进程的线程之间共享同一个内存空间。
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
- 一个进程挂了一般不会影响到其他进程。但是一个线程挂了,可能把同进程内的其他线程一起带走(整个进程崩溃)。
(4)Java 的线程和操作系统线程的关系
线程是操作系统中的概念。操作系统内核实现了线程这样的机制,并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库)。
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装。
二、创建线程
方法1继承Thread类
继承Thread来创建⼀个线程类:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("这⾥是线程运⾏的代码");
}
}
创建MyThread类的实例
MyThread t = new MyThread();
调⽤start⽅法启动线程
t.start(); // 线程开始运⾏
方法2实现Runnable接口
1.实现Runnable接⼝
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这⾥是线程运⾏的代码");
}
}
- 创建Thread类实例,调⽤Thread的构造⽅法时将Runnable对象作为target参数.
Thread t = new Thread(new MyRunnable());
- 调⽤start⽅法
t.start(); // 线程开始运⾏
对比上面两种方法:
- 继承 Thread 类,直接使用 this 就表示当前线程对象的引用。
- 实现 Runnable 接口,this 表示的是 MyRunnable 的引用。需要使用 Thread.currentThread ()。
其他变形
- 匿名内部类创建 Thread 子类对象
// 使用匿名类创建Thread子类对象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使用匿名类创建Thread子类对象");
}
};
- 匿名内部类创建 Runnable 子类对象
// 使用匿名类创建Runnable子类对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名类创建Runnable子类对象");
}
});
- lambda 表达式创建 Runnable 子类对象
// 使用lambda表达式创建Runnable子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建Thread子类对象"));
Thread t4 = new Thread(() -> {
System.out.println("使用匿名类创建Thread子类对象");
});
在这里插入代码片
四、多线程的优势-增加运⾏速度
可以观察多线程在⼀些场合下是可以提⾼程序的整体运⾏效率的。
- 使用 System.nanoTime () 可以记录当前系统的纳秒级时间戳。
- serial 串行的完成一系列运算。concurrency 使用两个线程并行的完成同样的运算。
public class ThreadAdvantage {
// 多线程并不一定就能提高速度,可以观察,count不同,实际的运行效果也是不同的
private static final long count = 10_0000_0000;
public static void main(String[] args) throws InterruptedException {
// 使用并发方式
concurrency();
// 使用串行方式
serial();
}
private static void concurrency() throws InterruptedException {
long begin = System.nanoTime();
// 利用一个线程计算a的值
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < count; i++) {
a--;
}
}
});
thread.start();
//主线程内计算b的值
int b = 0;
for (int i = 0; i < count; i++) {
b--;
}
//等待thread线程运行结束
thread.join();
//统计耗时
Long end = System.nanoTime();
double ms = (end - begin) * 1.0 / 1000 / 1000;
System.out.printf("并发:%f毫秒%n", ms);
}
private static void serial() {
//全部在主线程内计算a、b的值
Long begin = System.nanoTime();
int a = 0;
for (int i = 0; i < count; i++) {
a--;
}
int b = 0;
for (int i = 0; i < count; i++) {
b--;
}
Long end = System.nanoTime();
double ms = (end - begin) * 1.0 / 1000 / 1000;
System.out.printf("串行:%f毫秒%n", ms);
}
}
多运行几次,可见效率差别之大



554






