文章目录
一、概念
Java中的线程,是以轻量级进程来实现的
其他语言,可以是其他方式来实现,比如go,是协程的方式来实现(用户自己编写程序来实现线程,及线程的调度)
1.进程和线程的关联关系/区别
- 进程包含线程,一个进程至少包含一个线程
- 进程是系统分配资源的最小单位(基本单位),线程是操作系统调度cpu执行的最小单位(基本单位)
- 进程状态的改变会耗费很多时间,线程的效率更高
- 进程占用独立的虚拟地址空间,同一个进程中的多个线程可以共享这个进程的内存。
(1)一个进程要访问另一个的数据,需要使用通信的方式(代价比较大);同一个进程的线程,可以直接使用共享变量
(2)一个进程挂掉不会影响其他进程,但一个进程中的线程挂掉,就可能影响整个进程挂掉(比如一个线程申请的内存太多,超出整个进程的内存,就会挂掉(OOM))
线程和进程的状态:
线程和进程一样,系统也会创建pcb来管理
就绪:线程pcb不在队列的首部
运行:线程pcb处于cpu执行的时间片
状态改变比较耗时:
(1)创建
(2)销毁
(3)进入阻塞
线程和进程这些状态改变,都是比较耗时的,只是线程相对进程来说,耗时少一点
Java中,线程以轻量级进程的方式实现,也就具有进程的特征
需要系统调度cpu来执行:
- 并发:一个cpu以时间片轮转调度的方式,依次执行多个线程(人肉眼感知还是“同时”执行)
- 并行:多个cpu在一个时间点,同时执行多个线程
线程到底是就绪态,还是运行态,是操作系统调度决定,程序不知道
Java中,经常说并发编程,其实既包含多线程的并发,也有多线程的并行
二、创建线程
Java中,创建一个线程:
首先,Java程序要运行,必须先有一个main方法的入口类,先执行main方法(这里也会创建一个main线程)
是Java虚拟机创建的线程,然后执行main方法
1.继承Thread类
new Thread:创建一个Java中的线程对象(此时还没有创建系统中的线程(pcb))
2.实现Runnable接口
3.其他变形
3.本质还是继承Thread(匿名内部类)
4.实现Runnable接口(匿名内部类)
5.继承Thread(lambda表达式)
6.实现Runnable(lambda表达式)
4.总结
在我们理解上:
new Thread才能创建一个Java中的线程对象,调用start才会创建系统的线程,并申请系统调度执行
Runnable只是一个描述任务的对象,不是线程!
创建线程的方式:
- 继承Thread,重写run方法
- 实现Runnable接口,重写run方法
- 实现Callable接口(之后会补充)
三、多线程的作用/优势:增加运行速度
- 多个线程可以并发并行的执行,提高执行效率
main线程,总共循环20亿次++:
public class 多线程的优势_不使用多线程 {
public static void main(String[] args) {
//执行两次,每次循环++10亿次
int num = 10_0000_0000;
//记录执行的时间
//返回这行代码执行时,从1970-01-01经过的毫秒数
long start = System.currentTimeMillis();
for(int i=0;i<2;i++){
for(int j=0;j<num;j++){
//这里不需要任何代码,j已经++10亿次了
}
}
long end = System.currentTimeMillis();
System.out.printf("执行时间:%s\n",end-start);
}
}
两个线程同时并发并行的执行10亿次++:
public class 多线程的优势_使用多线程 {
public static void main(String[] args) {
//分别使用两个线程,每个执行10亿次++
//开始的执行时间
long start = System.currentTimeMillis();
for(int i=0;i<2;i++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for(int j=0;j<10_0000_0000;j++){
}
}
});
}
//这里不能直接记录结束时间(注意:要使用debug运行,不能使用run)
//以下代码的意思,是等两个thread线程执行完,main线程在往下执行
//线程有main,两个new Thread创建的线程
while(Thread.activeCount()>1){//activeCount返回活跃线程数
Thread.yield();//main就让步(从运行态转为就绪态)
}
long end = System.currentTimeMillis();
System.out.printf("执行时间:%s\n",end-start);
}
}
每次++次数很大的时候,使用多线程效率高
++次数少的时候,不使用多线程效率高
多个线程执行任务,任务执行时间越长,相对线程的创建,占用的时间比例就越小,效率就更高
(任务执行时间很快,比如执行一次++,创建的时间就相对更多)
有多个耗时的任务,使用多线程效率更高
四、观察多线程并发并行的特征
两个线程,每个循环10次打印线程名和循环次数
从表现看:
单个线程执行顺序还是代码书写的顺序
多个线程代码之间,执行的顺序,就是并发并行的执行(随机)
public class 多线程并发特性_1 {
public static void main(String[] args) {
//设计两个线程,每个循环10次,每次打印一个语句
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//Thread.currentThread返回这行代码运行时所在的线程引用
//thread.getName返回线程的名称
String name = Thread.currentThread().getName();
for(int i=0;i<10;i++){
try {
Thread.sleep(10);
System.out.printf("线程:%s,执行第 %s 次\n",name,i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t1");//创建线程时设置第二个参数,表示设置的线程名称
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//Thread.currentThread返回这行代码运行时所在的线程引用
//thread.getName返回线程的名称
String name = Thread.currentThread().getName();
for(int i=0;i<10;i++){
try {
Thread.sleep(100);
System.out.printf("线程:%s,执行第 %s 次\n",name,i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"t2");//创建线程时设置第二个参数,表示设置的线程名称
t1.start();
t2.start();
}
}
执行结果:
五、多线程并发中创建耗时