本节目标(全是重点,都必须掌握)
1、了解什么是线程、多线程、进程以及他们之间的关系
2、了解多线程的优势以及各种特性
3、用Java掌握多种创建线程的方法
一、线程、多线程、进程
1、概念
1.基本概念
这三个名词的概念可以用一个餐馆的厨房和厨师来进行超级形象的比喻。
想象一下一个餐馆的厨房。
2.线程、多线程、进程之间的区别
我们在梳理一下刚才所讲的比喻:
- 所以一个进程至少包含一个线程,既主线程。
- 不同的进程之间是不共享资源的(比如硬盘,网络资源等),但是一个进程中的每一个线程共享同一份资源。
- 线程是系统进行任务调度的最小单位。
- 进程是系统进行资源分配的最小单位。
- 一个进程挂了一般不会影响其他进程的正常运行,但是一个进程中的一个线程挂了,可能会影响到这个进程的正常运行!
- 任意一个线程,都可以在创建一个新的线程。
- 线程分为前台线程和后台线程,所有前台线程结束,整个进程才会结束,后台线程的结束,不会影响到进程的运行
注意:前台线程和后台线程除了是否能决定进程能否退出,在其他方面没有任何区别。
2、多线程的优点
首先,还是用刚才厨房和厨师的例子,假如说一个餐厅的客人非常的多,但是这么大的后厨,和这么多的需求只有一个厨师在卖命的炒菜,这显然是会被人骂的,餐厅早晚会倒闭。
为了充分利用厨房的各种资源,把每个厨具都用上,提高出餐效率,就可以请做个厨师同时进行工作。
既,充分利用进程所申请到的硬件资源(尤其是cup内核),多线程并发执行,高效完成任务,避免资源的浪费。
其次,虽然多进程也是可以实现并发编程的,但是进程的创建、调度、销毁速度远远慢与线程。既,线程更加轻量。
二、线程的创建方式
在java中创建线程,首先要了解一个类(Thread)和一个函数式接口(Runnable)
他们都有一个run()方法(Runnable的runf方法是抽象的,Thread的run方法是具体的),想要创建线程,就必须重写这个run()方法,然后把对象赋值给Thread类,通过Thread类的start()方法创建线程。
方式1——继承Thread:
class MyThread extends Thread{
@Override
public void run(){
while(true){
System.out.println("MuCreate");
}
}
}
public class Threads {
public static void main(String[] args) {
MyThread t1=new MyThread();
t1.start();
while(true){
System.out.println("主线程执行");
}
}
}
运行结果(截取部分)
以上是通过继承的方式。
Thread类有几个构造方法我们必须了解(等一下还会用到一个哦):
方式2——实现Runnable:
Runnable是一个函数式接口,只包含run这一个抽象方法(注意,Thread类中的run是一个具体的方法)
因此我们需要实现Runnable才能重写run方法:
class MyThread implements Runnable {//实现并重写run
@Override
public void run() {
while (true) {
System.out.println("MuCreate");
}
}
}
public class Threads {
public static void main(String[] args) {
/*下面两种创建方式都可以*/
//MyThread MyT=new MyThread();
//Thread t1=new Thread(MyT);//把接口对消给到Thread的构造方法
Thread t1 = new Thread(new MyThread());//把接口对消给到Thread的构造方法
t1.start();
while (true) {
System.out.println("主线程执行");
}
}
}
运行结果这里不演示了,和Thread的一样。
除了以上两种创建线程的方式,下面还有三种创建线程的方法需要学习,都需要学习掌握。
不过不用怕,下面这三种方式本质上没有区别,都是用到的Thread的这个构造方法:
方式3——匿名内部类之Runnable:
本质就是方式2,既用一个类实现Runnable,不过方式3的这个类没有名字。
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("我是手动创建的线程");
}
};
这段代码的含义是先创建一个匿名内部类,这个匿名内部类实现了Runnable接口,并且重写了run方法。然后把创建好的匿名内部类对象,重新赋值给了runnable,既向上转型。
然后再创建Thread类,调用刚才给的构造方法即可:
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("我是手动创建的线程");
}
};
Thread thread=new Thread(runnable);
thread.start();
当然也可以在Thread的构造方法中直接进行匿名内部类的编写,省去runnable的创建:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("我是手动创建的线程");
}
}
});
thread.start();
这样写的话代码的内聚性更强。
方式4——匿名内部类之Thread
本质就是方式1,既用一个类继承Thread,不过方式4的这个类没有名字。
Thread thread = new Thread(){
@Override
public void run(){
while(true){
System.out.println("创建的线程");
}
}
};
thread.start();
while(true){
System.out.println("主线程");
}
方式5——匿名内部类之lambda
方式5的创建和方式3其实是一样的,不过这里换成了lambda表达式
通过lambda表达式重写run方法,并创建一个Runnable的对象,通过赋值的方式,传递给Thread
类的构造方法。
Thread thread = new Thread(() -> {
//下面直接编写方法体
while (true) {
System.out.println("hehe");
}
});
thread.start();
不论是那种创建方式,都需要重写run()方法,run()方法相当于线程入口。
并且都必须通过Thread类调用start()方法才能启动线程,如果调用run()方法是不会创建线程的,它会仍然在主线程中运行run()方法中的程序。
完