多线程(1):
哈喽大家好,这篇文章就介绍一下多线程,因为多线程是一个难点,也很重要,所以将分为多篇文章来介绍和学习。
多线程是Java的核心特点之一。Java之所以能够发展很好是因为Java可以支持三高项目,高可用、高并
发、高性能。多线程就是实现高并发的基础技术。
什么是多线程?
在学习前,我们先对程序、进程、线程、并行、并发有个基础的概念了解:
- 程序: 为完成指定任务,用编程语言编写的一组指令的集合,即指一段静态的代码,静态对象。
- 进程: 是程序的一次执行过程,是一个动态的过程,进程自身有产生、使用和消亡的过程。(也称为生命周期,在后面会介绍)
- 线程: 进程可进一步细化为线程,是一个程序内部的一条执行路径,也就是进程内有线程
- 并行: 指两个或者多个事件在同一时刻发生,(同时发生)
- 并发: 指两个或者多个事件在同一个时段内发生,(并不是同时发生)
更好的理解进程和线程:
打开计算机任务管理器:
多线程有什么用?
我们就不看其他长篇大论,简单的说就是:节省时间,提高效率(在CPU满足的情况下同时执行多个任务)
举个例子:
12306大家肯定很熟悉,在高峰期的时候,会有几十万的用户同时在浏览和购票,假如你是第1000个进入购票系统的,系统不会让你等待前面999人买完你才可以买,不管你什么时候加入购票系统,随时都可以买,这就是多线程。而且CPU的运算速度让你感觉不用等待延迟(在CPU处理负载能力之内)。
多线程优点小结:
- 提高了用户的体验度,使程序的相应速度更快
- 占用大量的处理时间的任务可以使用多线程来提高CPU的使用效率
- 可以设优先级来优化性能
线程的生命周期:
线程的生命周期包括5个阶段: 新建、就绪、运行、阻塞、销毁
- 新建:在编译期间创建线程对象就属于新建状态,如Thread t1=new Thred()。(在下面介绍如何创建)
- 就绪: 当一个线程对象调用了start()方法就属于就绪状态,这时候线程处于等待CPU执行此线程的任务, 谁先抢到cpu资源就谁先执行,是随机的。
- 运行: 就绪状态的线程获得CPU资源执行的任务的时候就是运行状态,run()方法用于执行线程操作
- 阻塞状态:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态不能再继续执行任务, 这个时候其他线程获取CPU资源的可能性就更大了,CPU并不会等待阻塞状态的线程(任务)而去执行其他线程 的任务,也可以通过线程调度的机制“线程休眠”让运行状态下的线程任务变成阻塞,或者让阻塞状态的线 程“唤醒”变成就绪状态。直到再次等待CPU分配资源进入到运行状态。
- 销毁:如果线程正常执行完任务后或者线程被提前强制终止或者是因为出现异常导致线程任务 结束,都属于销毁状态。
关于阻塞状态的进一步理解:
假如你去超市买东西(创建),选完东西准备结账(就绪),你在付款了,但是的时候发现钱不够了(运行),不能完成支付,需要打电话给朋友借一点,因为你没有办法支付了(这就进入的阻塞状态),就不能进入下一步骤(走出超市,对应销毁)。
注意: 进入阻塞状态后并不是说不管你了,而是准备好后再次让CPU来处理你。就是你借到钱了,收银员会再次让你去付款。
创建多线程:
主线程:
当Java程序启动的时候,一个线程立刻执行,该线程叫做主线程(main Thread),因为他是程序开始就执行的。
主线程的作用:
- 产生其他子线程的线程
- 必须完成最后的执行,因为它还需要执行各种关闭动作
实现多线程有5种方式:
- 继承Thread父类重写run()方法,Java是单继承如果继承了Thread其他父类就不能再继承。(这种方式不建议)
- 实现Runable接口重写run()方法。(最常用)
- 实现Callable接口,调用有返回值的call方法
- .线程池创建多线程
- 通过Java8新特性Lambda表达式实现多线程
主线程:
public class test {
public static void main(String[] args) {
//获取当前线程的对象
Thread t = Thread.currentThread();
//输出当前线程的名字:
System.out.println(t.getName());
t.setName("我是主线程");
System.out.println(t.getName());
}
}
//输出:
//main
//我是主线程
通过上面的代码可以知道当前主线程的名字,也可以修改线程名、
关于Thread的方法介绍参考官方文档:
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html
1、继承Thread父类重写run()方法创建多线程:
继承Thread重写run方法:
//创建一个以main为主线程的子线程
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 1; i < 6; i++) {
System.out.println("我是子线程:"+i);
}
}
}
测试类:
public class test {
public static void main(String[] args) {
//创建子线程对象(新建状态)
MyThread t = new MyThread();
//就绪状态,等待CPU执行该线程任务
t.start();
for (int i = 1; i < 6; i++) {
System.out.println("main任务:主线程执行:"+i);
}
}
}
结果:
2、实现Runable接口重写run()方法(最常用)
创建子线程:
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 1; i < 10; i++) {
System.out.println("子线程-"+Thread.currentThread().getName()+i);
}
}
}
测试类:
public class test {
public static void main(String[] args) {
//创建子线程对象(新建状态)
MyThread t1 = new MyThread();
t1.setName("我是A线程:");
MyThread t2 = new MyThread();
t2.setName("我是B线程:");
MyThread t3 = new MyThread();
t3.setName("我是C线程:");
//就绪状态,等待CPU执行该线程任务
t1.start();
t2.start();
t3.start();
}
}
结果:
3、实现Callable<?>接口实现有返回值的多线程
public class MyThread implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
try{
//因为是泛型接口,call实 现了线程任务,并且返回值可以自定义
System.out.println("实现了Callable接口重写call方法");
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}
测试类:
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建对象:
MyThread myThread = new MyThread();
//通过线程池帮你创建了2个线程对象
//ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中
ExecutorService service = Executors.newFixedThreadPool(2);
//通过线程池对象调用submit方法,提交执行call方法的线程任务,最后返回结果
Future<Boolean> result1 = service.submit(myThread);
Future<Boolean> result2 = service.submit(myThread);
boolean r1= result1.get();
boolean r2= result2.get();
输出:
实现了Callable接口重写call方法
实现了Callable接口重写call方法
我是子线程1,返回的结果:true
我是子线程2,返回的结果:true
其他两个创建多线程的方式不怎么用,就不一一介绍,大家也可以看看其他技术博客学习~~
小结:
这篇文章主要将了多线程的概念以及创建多线程的方式,多线程还有很多的内容,就在下一篇再继续介绍啦!感谢阅读!
转载:https://blog.csdn.net/m0_57310550/article/details/123334907