欢迎来到“雪碧聊技术”CSDN博客!
在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。
让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!
目录
一、什么是多线程?
1、什么是线程?
2、什么是多线程?
3、多线程用在哪里?有什么用?
举例1:12306购票
举例2:百度网盘上传、下载同时完成
二、线程的创建方式1-继承Thread类
1、步骤
①创建一个类,继承Thread类,代表该类是一个线程类
②重写Thread类的run方法
③创建线程类的一个对象,代表一个新线程
④调用该对象的start方法,启动该线程
2、举例
3、注意事项
①启动线程必须是调用start方法,而不是调用run方法
②不要把主线程任务放在启动子线程之前
4、优缺点
三、线程的创建方式2-实现Runnable接口
1、步骤
①定义一个线程任务类,实现Runnable接口,并重写run方法
②创建MyRunnable任务对象
③把任务对象交给Thread类的构造器,将其封装为一个线程对象
④调用线程对象的start方法启动线程。
2、举例
3、优缺点
4、简化写法:匿名内部类写法
代码解释:
四、线程的创建方式3-实现Callable接口
1、前两种创建线程方式存在的问题
2、怎么解决这个问题?
一、什么是多线程?
1、什么是线程?
线程(Thread)是一个程序内部的一条执行流程。
举例:
2、什么是多线程?
多线程:指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。
3、多线程用在哪里?有什么用?
举例1:12306购票
网上买票的时候,肯定不止你自己,而是成千上万的人同时在购票,此时就需要12306的服务器提供很多的执行流程(即,很多线程)来进行购票业务的执行。
举例2:百度网盘上传、下载同时完成
一个线程负责下载,一个线程负责上传。
二、线程的创建方式1-继承Thread类
1、步骤
①创建一个类,继承Thread类,代表该类是一个线程类
②重写Thread类的run方法
③创建线程类的一个对象,代表一个新线程
④调用该对象的start方法,启动该线程
2、举例
public class test1 {public static void main(String[] args) {//目标:掌握线程的创建方式一:继承Thread类//4、创建线程类的对象,一个对象就是一个线程MyThread t1 = new MyThread();//5、调用对象的start方法,启动线程t1.start();//主线程(main函数所在的那条执行流程)代码for (int i = 0; i < 5; i++) {System.out.println("主线程输出:"+i);}}
}//1、定义一个子类继承Thread类,成为一个线程类
class MyThread extends Thread{//2、重写Thread类的run方法public void run() {//3、在run方法中编写线程的任务代码(线程要干的活)for (int i = 0; i < 5; i++) {System.out.println("子线程输出:"+i);}}
}
运行结果:
主线程输出:0
主线程输出:1
子线程输出:0
子线程输出:1
子线程输出:2
子线程输出:3
子线程输出:4
主线程输出:2
主线程输出:3
主线程输出:4
可见此时主线程和子线程是并发执行的关系,输出的for循环都是交叉的,因此这就是多线程。
3、注意事项
①启动线程必须是调用start方法,而不是调用run方法
简单来说:只有通过start方法,才能启动一个新的线程。而run方法不行。
- 直接调用run方法,会把该方法当成一个普通方法执行,此时相当于单线程。
- 只有调用start方法才是启动一个新的线程执行。
举例:
运行效果:
②不要把主线程任务放在启动子线程之前
- 这样主线程的任务先跑完,再启动子线程,那效果还是单线程的效果。
举例:
运行效果:
4、优缺点
- 优点:编码简单
- 缺点:存在单继承的局限性,线程类继承Thread后,不能再继承其他类,不便于功能扩展。
三、线程的创建方式2-实现Runnable接口
1、步骤
①定义一个线程任务类,实现Runnable接口,并重写run方法
②创建MyRunnable任务对象
③把任务对象交给Thread类的构造器,将其封装为一个线程对象
④调用线程对象的start方法启动线程。
2、举例
public class test2 {public static void main(String[] args) {//目标:掌握线程的创建方式2:实现Runnable接口//2、创建MyRunnable任务对象MyRunnable r = new MyRunnable();//3、把任务对象交给Thread类的构造器,将其封装为一个线程对象Thread t1 = new Thread(r);//4、调用线程对象的start方法启动线程。t1.start();//主线程的任务for (int i = 0; i < 5; i++) {System.out.println("主线程输出:"+i);}}
}//1、定义一个线程任务类,实现Runnable接口,并重写run方法
class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("子线程输出:"+i);}}
}
运行效果:
主线程输出:0
主线程输出:1
子线程输出:0
主线程输出:2
主线程输出:3
子线程输出:1
主线程输出:4
子线程输出:2
子线程输出:3
子线程输出:4
可见此时主线程、子线程交替输出,是并发的。
3、优缺点
- 优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
- 缺点:需要多一个实现Runnable接口的步骤
4、简化写法:匿名内部类写法
public class test3 {public static void main(String[] args) {//掌握:匿名内部类写法new Thread(new Runnable() {public void run() {for (int i = 0; i < 5; i++) {System.out.println("子线程输出:"+i);}}}).start();//主线程任务for (int i = 0; i < 5; i++) {System.out.println("主线程输出:"+i);}}
}
运行效果:
子线程输出:0
主线程输出:0
主线程输出:1
主线程输出:2
主线程输出:3
子线程输出:1
主线程输出:4
子线程输出:2
子线程输出:3
子线程输出:4
可见此时主线程和子线程还是交替输出的,是并发关系。
代码解释:
因此上述代码就可以理解为:
new Thread(任务类对象).start();
因此此时Thread类的构造器,会将任务类对象封装成一个线程对象,此时在调用该线程对象的.start方法,即可启动该线程。
四、线程的创建方式3-实现Callable接口
1、前两种创建线程方式存在的问题
线程执行完毕后有一些数据需要返回,他们重写的run方法均不能返回结果。
2、怎么解决这个问题?
JDK 5.Callable接口和FutureTast类来实现。