Java-宋红康-(P133-P134)-多线程创建方式(Thread and Runnable)

2023-12-06 00:04

本文主要是介绍Java-宋红康-(P133-P134)-多线程创建方式(Thread and Runnable),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

b站视频

133-多线程-线程创建方式1:继承Thread类_哔哩哔哩_bilibili

目录

3.1 继承Thread

3.1.1 继承Thread类方式

3.1.2 线程的执行流程

3.1.3 线程内存图

3.1.4 run()方法和start()方法

3.1.5 线程名字的设置和获取

3.1.6 获取运行main方法线程的名字

3.1.7 练习题

3.2 实现 Runable

3.2.1 实现Runnable接口方式

3.2.2 Thread和Runnable的对比

3.2.3 匿名内部类方式创建线程


3.1 继承Thread

3.1.1 继承Thread类方式

Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。

  2. 创建Thread子类的实例,即创建了线程对象

  3. 调用线程对象的start()方法来启动该线程

自定义线程类:

class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println("自定义线程正在执行!"+i);}}
}

测试类:

public class Main {public static void main(String[] args)  throws Exception{MyThread mt=new MyThread();mt.start();for (int i = 0; i < 20; i++) {System.out.println("main线程"+i);}}
}

结果:

3.1.2 线程的执行流程

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。

通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。

多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:

多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。

3.1.3 线程内存图

当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。

3.1.4 run()方法和start()方法

  • run()方法,是线程执行的任务方法,每个线程都会调用run()方法执行,我们将线程要执行的任务代码都写在run()方法中就可以被线程调用执行。

  • start()方法,开启线程,线程调用run()方法。start()方法源代码中会调用本地方法start0()来启动线程:private native void start0(),本地方法都是和操作系统交互的,因此可以看出每次开启一个线程的线程都会和操作系统进行交互。

注意:一个线程只能被启动一次!!

实例:

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//通过对象调用start()t1.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}

结果:

问题1:能否用t1.run()方法替换t1.start()的调用,实现分线程的创建和调用。

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//通过对象调用start()// t1.start();t1.run();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}

结果:(输出的全是main,通过调用run方法后变成了单线程)

3.1.5 线程名字的设置和获取

  • Thread类的方法String getName()可以获取到线程的名字。

  • Thread类的方法setName(String name)设置线程的名字。

  • 通过Thread类的构造方法Thread(String name)也可以设置线程的名字。

public class MyThread  extends Thread{public void run(){System.out.println("线程名字:"+super.getName());}
}

测试类:

public class Demo {public static void main(String[] args) {//创建自定义线程对象MyThread mt = new MyThread();//设置线程名字mt.setName("旺财");//开启新线程mt.start();}
}

注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,Thread-1。

3.1.6 获取运行main方法线程的名字

  • Demo类不是Thread的子类,因此不能使用getName()方法获取。

  • Thread类定义了静态方法static Thread currentThread()获取到当前正在执行的线程对象。

  • main方法也是被线程调用了,也是具有线程名字的。

public static void main(String[] args){Thread t = Thread.currentThread();System.out.println(t.getName());
} 

结果:

3.1.7 练习题

创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数。

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//创建当前Thread的子类的对象PrintOddNumber t2=new PrintOddNumber();//通过对象调用start()t1.start();//通过对象调用start()t2.start();}
}//遍历100以内的偶数
class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0) {System.out.println(Thread.currentThread().getName() +"--" + i);}}}
}//遍历100以内的奇数
class PrintOddNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==1) {System.out.println(Thread.currentThread().getName() +"--"+i);}}}
}

结果:

3.2 实现 Runable

3.2.1 实现Runnable接口方式

采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

  1. 创建一个实现Runnable接口的类

  2. 实现接口中的run()方法 --> 将此线程要执行的操作,声明在此方法体中

  3. 创建Runnable实现类的实例

  4. 将此对象实例作为参数传递到Thread类的构造器中,创建Thread类的实例

  5. Thread类的实例调用start()方法:①启动线程 ②调用当前线程的run()。

package test;public class TestDemo04 {public static void main(String[] args) {//③ 创建当前实现类的对象EvenNumberPrint evenNumberPrint=new EvenNumberPrint();//④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例Thread t1=new Thread(evenNumberPrint);//⑤ Thread类的实例调用start():1.启动线程 2. 调用当前线程的run()方法t1.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"---"+i);}}
}//① 创建一个实现Runnable接口的类
class EvenNumberPrint implements Runnable{//② 实现接口中的run() -->将此线程要执行的操作,声明在此方法中。@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"---"+i);}}
}

结果:

通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。

在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

3.2.2 Thread和Runnable的对比

共同点:

  • 启动线程,使用的都是Thread类中定义的start()方法

  • 创建的线程对象,都是Thread类或其子类的实例。

不同点:

一个是类的继承,一个是接口的实现。

建议:建议使用实现Runnable接口的方式

Runnable方式的好处:

①实现的方式,可以避免类的单继承的局限性。

②更适合多个相同的程序代码的线程去共享同一个资源。

③增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

联系: public class Thread implements Runnable(代理模式)

3.2.3 匿名内部类方式创建线程

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

public class NoNameInnerClassThread {public static void main(String[] args) {                   
//                new Runnable(){
//                        public void run(){
//                                for (int i = 0; i < 20; i++) {
//                                        System.out.println("张宇:"+i);
//                                }
//                        }  
//                   }; //---这个整体  相当于new MyRunnable()Runnable r = new Runnable(){public void run(){for (int i = 0; i < 20; i++) {System.out.println("张宇:"+i);}}  };new Thread(r).start();for (int i = 0; i < 20; i++) {System.out.println("费玉清:"+i);}}
}

这篇关于Java-宋红康-(P133-P134)-多线程创建方式(Thread and Runnable)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/459654

相关文章

使用FileChannel实现文件的复制和移动方式

《使用FileChannel实现文件的复制和移动方式》:本文主要介绍使用FileChannel实现文件的复制和移动方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录使用 FileChannel 实现文件复制代码解释使用 FileChannel 实现文件移动代码解释

Spring实现Bean的初始化和销毁的方式

《Spring实现Bean的初始化和销毁的方式》:本文主要介绍Spring实现Bean的初始化和销毁的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Bean的初始化二、Bean的销毁总结在前面的章节当中介绍完毕了ApplicationContext,也就

Java的"伪泛型"变"真泛型"后对性能的影响

《Java的伪泛型变真泛型后对性能的影响》泛型擦除本质上就是擦除与泛型相关的一切信息,例如参数化类型、类型变量等,Javac还将在需要时进行类型检查及强制类型转换,甚至在必要时会合成桥方法,这篇文章主... 目录1、真假泛型2、性能影响泛型存在于Java源代码中,在编译为字节码文件之前都会进行泛型擦除(ty

Java中的getBytes()方法使用详解

《Java中的getBytes()方法使用详解》:本文主要介绍Java中getBytes()方法使用的相关资料,getBytes()方法有多个重载形式,可以根据需要指定字符集来进行转换,文中通过代... 目录前言一、常见重载形式二、示例代码三、getBytes(Charset charset)和getByt

Java使用Stream流的Lambda语法进行List转Map的操作方式

《Java使用Stream流的Lambda语法进行List转Map的操作方式》:本文主要介绍Java使用Stream流的Lambda语法进行List转Map的操作方式,具有很好的参考价值,希望对大... 目录背景Stream流的Lambda语法应用实例1、定义要操作的UserDto2、ListChina编程转成M

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

idea报错java: 非法字符: ‘\ufeff‘的解决步骤以及说明

《idea报错java:非法字符:‘ufeff‘的解决步骤以及说明》:本文主要介绍idea报错java:非法字符:ufeff的解决步骤以及说明,文章详细解释了为什么在Java中会出现uf... 目录BOM是什么?1. BOM的作用2. 为什么会出现 \ufeff 错误?3. 如何解决 \ufeff 问题?最

使用Java编写一个字符脱敏工具类

《使用Java编写一个字符脱敏工具类》这篇文章主要为大家详细介绍了如何使用Java编写一个字符脱敏工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、字符脱敏工具类2、测试工具类3、测试结果1、字符脱敏工具类import lombok.extern.slf4j.Slf4j

Java实现按字节长度截取字符串

《Java实现按字节长度截取字符串》在Java中,由于字符串可能包含多字节字符,直接按字节长度截取可能会导致乱码或截取不准确的问题,下面我们就来看看几种按字节长度截取字符串的方法吧... 目录方法一:使用String的getBytes方法方法二:指定字符编码处理方法三:更精确的字符编码处理使用示例注意事项方