文章专栏
本篇文章收录于多线程,也欢迎翻阅博主的其他文章,可能也会让你有不一样的收获😄
💡JavaSE语法 💡数据结构 💡多线程
💡专栏导读
操作系统提供了一些API来操作线程,Java针对这些API进行了封装,所以只要掌握了这一套API就可以了,在Java中,JVM使用Thread类管理线程, 换句话讲,每一个Thread对象都对应一个线程,JVM将这些Thread对象组织起来进行调度执行, 想要使用线程,只要学习这个类中的方法就可以了
💐创建线程的五种方式
方法 | 解释 |
---|---|
Thread() | 创建线程对象 |
Thread(String name) | 创建线程对象,并给线程命名,不会影响线程 |
Thread(Runnable runnable) | 使用Runnable对象创建线程 |
Thread(Runnable runnable, String name) | 使用Runnable对象创建线程并给线程命名 |
方式1.创建一个类,继承Thread
每一个线程都是一个单独的”执行流“, 都可以执行一段代码逻辑,但是,创建的线程从哪里开始执行呢???就需要提供一个入口,而run方法就是线程的入口,表示就从run方法这里开始执行;这一点就像是main方法一样,当运行一个Java程序时,就会从main方法开始执行代码;
此时,我们写的一个Java程序其实就是一个进程,一个进程里面至少要有一个线程,就是主线程,而在Java程序中,主线程的入口方法就是main方法;
//创建一个类,继承Thread
class MyThread extends Thread{
//重写Thread里面的run方法,这个方法就是线程的入口
@Override
public void run() {
//代码逻辑
}
}
public class Demo1 {
public static void main(String[] args) {
}
}
但是,只有run方法还是不够的,run方法只是说明了线程从哪个地方开始执行,run方法里面只是定义了线程要做哪些任务,在主线程中,需要再调用start()方法来创建一个新的线程,线程创建后,再由线程调用run()方法开始执行任务;
//创建一个类,继承Thread
class MyThread extends Thread{
//重写Thread里面的run方法,这个方法就是线程的入口
@Override
public void run() {
System.out.println("线程正在工作");
}
}
public class Demo1 {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
//run() 和 start() 都是Thread的方法
//run()方法只是线程的入口
//start()方法才是调用了系统API来创建一个线程,让线程再调用run来执行任务
}
}
下面展示一个两个线程并发执行的情况:
class MyThread extends Thread{
//重写Thread里面的run方法,这个方法就是线程的入口
@Override
public void run() {
//写一个while循环让这个线程一直打印
while(true) {
System.out.println("hello Thread");
try {
//进行一个休眠方便观察
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
//写一个while循环,一直打印
while(true) {
System.out.println("hello main");
//进行一个休眠方便观察
Thread.sleep(1000);
}
}
}
上述结果就可以看到,两个线程分别在执行自己的代码,这也就印证了每一个线程都是一个单独的”执行流“, 本来在运行时只有一个执行流,但是遇见 thread.start() ,就开始了兵分两路”同时执行“, 就达到了并发编程的效果,而这里的并发指的是”并行+并发“, 因为,程序员在写代码的时候,是无法感知到这两个线程到底是在一个CPU核心上调度执行还是在两个CPU核心上同时执行, 所以在宏观上也就是我们人的肉眼看着都是在同时执行的,这一点在进程那篇文章中已经讲解的很清楚了,你可以去看一下👈
如果讲代码中的 thread.start() 改为 thread.run() 会发生什么呢?
如果改成thread.run(), 它就只剩下一个主线程了,只会按照代码的顺序依次执行,就成了一个单独的方法调用,这就不是多线程工作了,只有当run()方法结束后,才会继续向下执行;也就没有实现”并发编程”;也可以看到,Runnable类中也是只有一个方法run();
方式2.创建一个类,实现Runnable接口
使用Runnable接口和继承Thread的区别就是为了解耦合,比如说,有一些任务不一定是线程调度执行,也有可能是线程池或着其他等等,这时候就可以把这个任务给抽离出来,直接就可以用线程池或其他;
//创建一个类,实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
while(true) {
System.out.println("hello Thread");
}
}
}
public class Demo2 {
public static void main(String[] args) {
//Runnable 就表示的是这是一个可执行的任务
Runnable runnable = new MyThread();
Thread thread = new Thread(runnable);
thread.start();
while(true) {
System.out.println("hello main");
}
}
}
方式3.继承Thread,重写run(), 但是使用匿名内部类
这个类没有名字,所以叫匿名内部类,虽然没有名字但是也没有关系,因为这个类是一次性的,只知道这个类是Thread的子类,然后同时又进行了实例化,之后在子类中重写run() 方法;如果你不理解匿名内部类,可以我这篇文章👉
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("hello Thread");
}
};
thread.start();
System.out.println("hello main");
}
方法4.实现Runnable,重写run(), 但是使用匿名内部类
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello Thread");
}
};
Thread thread = new Thread(runnable);
thread.start();
System.out.println("hello main");
}
方式5.使用Lambda表达式
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("hello Thread");
});
thread.start();
System.out.println("hello main");
}