• Java中的多线程


    目录

    1、理解线程和多线程

    2、多线程的创建

    Thread类

    多线程的实现方案一:继承Thread类

     多线程的实现方案二:实现Runnable接口

     多线程的实现方案二:实现Runnable接口(匿名内部类形式)

    多线程的实现方案三:利用Callable、FutureTask接口实现。 

     3种方式对比


    1、理解线程和多线程

    什么是线程?

    线程(thread)是一个程序内部的一条执行路径

    我们启动程序执行后,main方法的执行其实就是一条单独的执行路径。

    程序中如果只有一条执行路径,那么这个程序就是单线程的程序。

    如:

    1. public static void main(String[] args) {
    2. // 代码
    3. for (int i = 0; i < 10; i++) {
    4. System.out.println(i);
    5. }
    6. // 代码...
    7. }

    多线程是什么?

    多线程是指从软硬件上实现多条执行流程的技术。

    2、多线程的创建

    Thread类

    构造器:

    public Thread(String name):可以为当前线程指定名称
    public Thread(Runnable target):封装Runnable对象成为线程对象
    public Thread(Runnable target ,String name ):封装Runnable对象成为线程对象,并指定线程名称

    常用方法:

    String getName():获取当前线程的名称,默认线程名称是Thread-索引
    void setName(String name):将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称
    public static Thread currentThread():返回对当前正在执行的线程对象的引用
             1、此方法是Thread类的静态方法,可以直接使用Thread类调用。
             2、这个方法是在哪个线程执行中调用的,就会得到哪个线程对象。

    public static void sleep(long time):让当前线程休眠指定的时间后再继续执行,单位为毫秒。

    多线程的实现方案一:继承Thread

    步骤:
    定义一个子类 MyThread 继承线程类 java.lang.Thread ,重写 run() 方法
    创建 MyThread 类的对象
    调用线程对象的 start() 方法启动线程(启动后还是执行 run 方法的)
    注意:

    1、为什么不直接调用run方法,而是调用start启动线程。

    直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。

    只有调用start方法才是启动一个新的线程执行。

    2、不能把主线程任务放在子线程之前。

    如果主线程放在前面,这样主线程一直是先跑完的,相当于是一个单线程的效果。

    3、优缺点是什么?

    优点:编码简单

    缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展

    具体代码实现: 

    1. public class ThreadDemo1 {
    2. public static void main(String[] args) {
    3. //2、new一个新线程对象
    4. Thread t = new MyThread();
    5. //3、调用start方法启动线程(执行的还是run()方法
    6. t.start();
    7. for (int i = 0; i < 5; i++) {
    8. System.out.println("主线程执行输出:" + i);
    9. }
    10. }
    11. }
    12. //1、定义一个子类MyThread继承线程类java.lang.Thread,
    13. class MyThread extends Thread{
    14. //重写run()方法
    15. @Override
    16. public void run() {
    17. for (int i = 0; i < 5; i++) {
    18. System.out.println("子线程执行输出:" + i);
    19. }
    20. }
    21. }

     多线程的实现方案二:实现Runnable接口

    步骤:
    定义一个线程任务类 MyRunnable 实现 Runnable 接口,重写 run() 方法
    创建 MyRunnable 任务对象
    MyRunnable 任务对象交给 Thread 处理。
    调用线程对象的 start() 方法启动线程

    优缺点:
    优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
     缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

    具体代码实现: 

    1. public class RunnableDemo1 {
    2. public static void main(String[] args) {
    3. //2、创建一个任务对象
    4. MyRunnable target = new MyRunnable();
    5. //3、把任务对象交给Thread处理
    6. Thread t = new Thread(target);
    7. //4、启动线程
    8. t.start();
    9. for (int i = 0; i < 5; i++) {
    10. System.out.println("主线程执行输出:" + i);
    11. }
    12. }
    13. }
    14. //1、定义一个线程任务类MyRunnable实现Runnable接口
    15. class MyRunnable implements Runnable{
    16. //重写run()方法
    17. @Override
    18. public void run() {
    19. for (int i = 0; i < 5; i++) {
    20. System.out.println("子线程执行输出:" + i);
    21. }
    22. }
    23. }

     多线程的实现方案二:实现Runnable接口(匿名内部类形式)

    步骤:
    创建 Runnable 的匿名内部类对象。
    交给 Thread 处理。
    调用线程对象的 start() 启动线程。

    具体代码实现: 

    1. public class RunnableDemo2 {
    2. public static void main(String[] args) {
    3. //写法1,子线程1
    4. Runnable target = new Runnable() {
    5. @Override
    6. public void run() {
    7. for (int i = 0; i < 5; i++) {
    8. System.out.println("子线程1执行输出:" + i);
    9. }
    10. }
    11. };
    12. Thread t = new Thread(target);
    13. t.start();
    14. //写法2,子线程2
    15. new Thread(new Runnable() {
    16. @Override
    17. public void run() {
    18. for (int i = 0; i < 5; i++) {
    19. System.out.println("子线程2执行输出:" + i);
    20. }
    21. }
    22. }).start();
    23. //写法3,子线程3
    24. new Thread(() -> {
    25. for (int i = 0; i < 5; i++) {
    26. System.out.println("子线程3执行输出:" + i);
    27. }
    28. }).start();
    29. //主线程
    30. for (int i = 0; i < 5; i++) {
    31. System.out.println("主线程执行输出:" + i);
    32. }
    33. }
    34. }

    1、前2种线程创建方式都存在一个问题:

    他们重写的 run 方法均不能直接返回结果。
    不适合需要返回线程执行结果的业务场景。

    2、怎么解决这个问题呢?

    JDK 5.0 提供了 Callable FutureTask 来实现。
    这种方式的优点是:可以得到线程执行的结果。

    多线程的实现方案三:利用CallableFutureTask接口实现。 

    步骤:
    、得到任务对象
    1. 定义类实现 Callable 接口,重写 call 方法,封装要做的事情。
    2. FutureTask Callable 对象封装成线程任务对象。
    、把线程任务对象交给 Thread 处理。
    、调用 Thread start 方法启动线程,执行任务
    、线程执行完毕后、通过 FutureTask get 方法去获取任务执行的结果。
    FutureTask API:

    public FutureTask<>(Callable call):把Callable对象封装成FutureTask对象。
    public V get() throws Exception:获取线程执行call方法返回的结果。

    优缺点:
    优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
    可以在线程执行完毕后去获取线程执行的结果。
    缺点:编码复杂一点。

    具体代码实现: 

    1. public class CallableDemo1 {
    2. public static void main(String[] args) {
    3. //3、创建Callable任务对象
    4. Callable<String> call = new MyCallable(100);
    5. //4、把Callable任务对象 交给FutureTask对象
    6. // FutureTask对象的作用1:是Runnable的对象(实现了Runnable的接口),可以交给Thread了
    7. // FutureTask对象的作用2:可以在线程执行完毕之后通过调用其get()方法得到线程执行完成的结果
    8. FutureTask<String> f1 = new FutureTask<>(call);
    9. //5、交给线程处理
    10. Thread t = new Thread(f1);
    11. //6、启动线程
    12. t.start();
    13. Callable<String> call1 = new MyCallable(200);
    14. FutureTask<String> f2 = new FutureTask<>(call1);
    15. Thread t1 = new Thread(f2);
    16. t1.start();
    17. try {
    18. //如果f1任务没有执行完毕,这里的代码回等待,直到线程1跑完才提取结果
    19. String s1 = f1.get();
    20. System.out.println("第一个结果:" + s1);
    21. } catch (Exception e) {
    22. e.printStackTrace();
    23. }
    24. try {
    25. //如果f2任务没有执行完毕,这里的代码回等待,直到线程2跑完才提取结果
    26. String s2 = f2.get();
    27. System.out.println("第二个结果:" + s2);
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }
    33. //1、定义一个任务类实现Callable接口, 应该声明线程任务类执行完毕后的结果的数据类型
    34. class MyCallable implements Callable<String>{
    35. private int n;
    36. public MyCallable(int n) {
    37. this.n = n;
    38. }
    39. //重写call()方法(任务方法)
    40. @Override
    41. public String call() throws Exception {
    42. int sum = 0;
    43. for (int i = 1; i <= n; i++) {
    44. sum += i;
    45. }
    46. return "子线程执行的结果是:" + sum;
    47. }
    48. }

     3种方式对比

  • 相关阅读:
    vue+elementUI实现级联表格el-table级联多选
    自然语言处理(NLP)—— 神经网络语言处理
    ElasticSearch可视化管理工具cerebro
    selenium安装配置及基本使用
    户外运动耳机推荐、这几款性能超强的户外运动耳机不可错过
    一篇搞定Sentinel-搭建Spring Cloud Alibaba服务组件Sentinel实现服务资源控制
    【DDD】学习笔记-发布者—订阅者模式
    docker centos host setting
    ABAP内表排序
    我想咨询一下Python,请问在哪儿找资源比较好呀?
  • 原文地址:https://blog.csdn.net/weixin_62023527/article/details/126517924