当前位置: 首页 > article >正文

多线程的创建方式以及及Thread类详解

目录

一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

写法二:匿名内部类 

二.实现Runnable接口 

写法一:正常写法

 写法二:匿名内部类 

三. 实现 Callable 接口 

​编辑

四.通过线程池创建线程

二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

2.Thread的⼏个常⻅属性

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

join()方法

 中断线程

sleep()方法 


一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务

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

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

public class MyThread  extends  Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("这里是子线程");
        }
    }
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.start();
        while (true) {
            System.out.println("这里是主线程");
        }
    }
}

 

写法二:匿名内部类 

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("这里是thread线程");
                }
            }
        };
        thread.start();
        while (true) {
            System.out.println("这里是main线程");
        }
    }


二.实现Runnable接口 

写法一:正常写法

  1. 创建一个实现了Runnable接口的类,并实现接口中的run()方法,定义线程的执行逻辑。
  2. 在主线程中创建Runnable实例,并将其作为参数传递给Thread类的构造方法。
  3. 调用Thread对象的start()方法启动线程。
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        while (true) {
            System.out.println("thread!");
        }
    }

    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        while (true) {
            System.out.println("main!");
        }
    }
}

 

 写法二:匿名内部类 

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("thread!");
                }
            }
        });
        thread.start();
        while (true) {
            System.out.println("main!");
        }
    }

三. 实现 Callable 接口 

前两种方式都存在一个问题:重写的run方法均不能直接返回结果,不适合需要返回线程执行结果的场景。而通过实现Callable接口则可以做到这一点。

  1. 得到任务对象定义类实现Callable接口,重写call方法,封装要做的事情,用FutureTask把Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 3; i++) {
            System.out.println("thread:" + i);
        }
        return "call!";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println("main:"+i);
        }
        System.out.println(futureTask.get());
    }
}


四.通过线程池创建线程

后序再详讲


二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

  • Thread t1 = new Thread();
  • Thread t2 = new Thread(new MyRunnable());
  • Thread t3 = new Thread("这是我的名字");
  • Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.Thread的⼏个常⻅属性

  •  ID是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  •  状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
  •  优先级⾼的线程理论上来说更容易被调度到
  •  关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为run⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明 

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

  • start()方法是Thread类中的一个方法,用于启动一个新的线程。当调用start()方法时,系统会创建一个新的线程,并在新的线程中执行run()方法的内容。start()方法会在新的线程中执行一些准备工作,然后调用run()方法。
  • run()方法是实现了Runnable接口的类中的一个方法。在启动一个线程后,系统会自动调用该线程对象的run()方法。run()方法中包含了线程的主体代码,即线程要执行的任务。

2.start()方法

public class MyThread extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("这里是thread");
        }
    }
    public static void main(String[] args) {
        MyThread thread = new MyThread() ;
        thread.start();
        while (true) {
            System.out.println("这里是main线程");
        }
    }
}

此时为俩个线程并发处理

1.run()方法

 

public class MyThread extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("这里是thread");
        }
    }
    public static void main(String[] args) {
        MyThread thread = new MyThread() ;
        thread.run();
        while (true) {
            System.out.println("这里是main线程");
        }
    }
}

此时只实现了run方法 并没有并发处理 

总结:

  • start() 方法 用于启动一个新线程,它会触发线程的创建和调度,最终由操作系统负责执行 run() 方法。
  • run() 方法 是线程的实际执行任务,是线程执行的代码部分。如果直接调用 run() 方法,它会在当前线程中执行,并不会启动新的线程。

join()方法

在多个线程中,他们的执行顺序属于系统调动的,是无序的(抢占式执行)

而我们希望一个稳定的顺序,如何执行?

join() 方法的使用场景:

场景 1:确保多个线程按顺序执行

有时,你可能希望在主线程中启动多个子线程,并确保在继续执行后续任务之前,所有子线程已经完成。例如,在处理多个任务并且它们之间有依赖关系时,必须等待所有线程完成。

场景 2:等待线程完成后汇总结果

在多线程并发计算时,主线程可能需要等待多个线程完成任务,才能进行汇总或处理结果。join() 方法可以帮助你等待所有线程执行完成后再进行下一步操作。

场景 3:避免主线程提前退出

如果没有使用 join(),主线程可能会在子线程完成之前就退出,导致子线程未执行完成。使用 join() 可以保证主线程在所有子线程完成之前不会退出。

 

案例:

class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    public void run() {
        System.out.println(name + " 正在执行...");
        try {
            Thread.sleep(1000); // 模拟任务执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + " 执行完毕!");
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread("线程1");
        MyThread thread2 = new MyThread("线程2");
        MyThread thread3 = new MyThread("线程3");

        thread1.start();
        thread2.start();
        thread3.start();

        // 主线程等待所有子线程执行完毕
        thread1.join();
        thread2.join();
        thread3.join();

        System.out.println("所有线程都已执行完毕,主线程继续执行...");
    }
}

  • 在这个例子中,主线程通过调用 thread1.join()thread2.join() 和 thread3.join() 来等待所有子线程执行完毕。
  • 主线程会阻塞,直到 thread1thread2 和 thread3 执行完成。
  • 当所有线程执行完毕后,主线程输出 "所有线程都已执行完毕,主线程继续执行..."

总结:

  1. 确保线程顺序:有时我们希望线程按照特定的顺序执行。使用 join() 可以让主线程等待子线程执行完毕,这样就能保证主线程在子线程之后才继续执行。

  2. 避免主线程提前退出:在多线程应用中,如果主线程没有等待子线程完成就退出,可能导致子线程未完成时程序就结束了,特别是在没有使用 join() 时,主线程可能比子线程先结束。

  3. 汇总线程结果:如果多个线程在并行执行,主线程可能需要等待它们执行完毕后再进行结果汇总。join() 方法能够确保线程完成后,主线程能够获取到所有线程的执行结果。

  4. 避免线程竞态条件:通过确保线程按顺序执行,join() 可以避免一些可能的竞态条件和线程同步问题。


 中断线程

在 Java 多线程编程中,中断线程是一种用于请求线程停止执行的机制。线程的中断并不意味着直接停止线程的执行,而是给线程发送一个中断信号,让线程有机会在合适的地方处理这个信号并终止。线程中断通常用于协调多个线程的生命周期,特别是在需要停止一个线程的执行时,或者是某个线程的操作不再需要时

线程中断的使用场景:

  1. 停止正在执行的线程: 当线程正在进行耗时的任务时,如果你需要在某个时刻停止它,可以通过中断来实现。中断信号告诉线程它可以停止工作或清理资源并退出。

    应用场景:比如有一个后台线程在执行某些长时间的计算任务或下载操作,如果用户取消了操作,你可以通过中断线程来停止这些任务。

  2. 响应外部中断请求: 比如在一个多线程的客户端应用中,如果用户要求退出程序或者停止某些操作,可以通过发送中断信号来优雅地停止正在运行的线程。

    应用场景:比如在一个多线程的 HTTP 请求处理系统中,如果服务器需要停止处理请求,可以通过中断正在处理请求的线程。

  3. 在阻塞操作中响应中断: 当线程执行如 sleep()join()wait() 等阻塞操作时,可以中断线程来提前终止阻塞操作,从而使线程能够继续执行其他任务。

    应用场景:在多线程的网络编程中,可能需要等待其他线程的响应,如果等待超时,则可以通过中断等待线程,防止程序长时间处于等待状态。

  4. 取消正在执行的任务: 在一些任务执行框架中,可以通过中断来取消正在执行的任务。当任务执行时间过长,或者任务本身不再需要时,可以通过中断来提示线程终止。

    应用场景:比如在处理大量数据时,当用户主动取消任务时,可以中断相关的线程。

 案例1:使用 Thread.interrupt() 中断线程

public class InterruptExample {
    public static void main(String[] args) {
        // 创建线程
        Thread thread = new Thread(() -> {
            try {
                System.out.println("线程开始执行");
                // 模拟一个长时间的任务
                for (int i = 0; i < 10; i++) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("线程被中断,退出循环");
                        return;
                    }
                    Thread.sleep(1000);  // 模拟工作
                    System.out.println("线程正在工作,执行第 " + (i + 1) + " 次");
                }
            } catch (InterruptedException e) {
                System.out.println("线程在阻塞时被中断:" + e.getMessage());
            }
        });

        thread.start();

        try {
            // 等待一会儿,然后中断线程
            Thread.sleep(3000);
            thread.interrupt();  // 请求中断线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

示例 2:线程阻塞时的中断 

public class InterruptInSleepExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("线程开始执行,准备休眠");
                Thread.sleep(5000);  // 模拟长时间的阻塞操作
                System.out.println("线程正常执行完毕");
            } catch (InterruptedException e) {
                System.out.println("线程被中断,异常信息:" + e.getMessage());
            }
        });

        thread.start();

        try {
            // 等待 2 秒钟然后中断线程
            Thread.sleep(2000);
            thread.interrupt();  // 请求中断线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

总结

  • 中断线程是通过 Thread.interrupt() 方法发出请求,线程可以通过检查中断状态来响应这个请求。
  • 常见的使用场景包括:停止长时间运行的任务、在阻塞操作中响应外部请求、取消正在进行的操作等。
  • 线程中断是一种“协作式的停止”,即线程需要在合适的地方主动检查中断状态并退出。

sleep()方法 

在Java中,sleep() 方法是 Thread 类中的一个静态方法,用于暂停当前执行的线程指定的时间

案例:定时打印信息

public class TimerTaskExample {
    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        for (int i = 1; i <= 5; i++) {
            System.out.println("第 " + i + " 次执行");
            try {
                // 每次执行后休眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.println("总共耗时:"+(end-start));
    }
}


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力! 


http://www.kler.cn/a/388392.html

相关文章:

  • 赛博错题本
  • stm32基础(keil创建、Proteus仿真、点亮LED灯,7段数码管)
  • QT-基础-1-Qt 中的字符串处理与常见数据类型
  • HW护网分析研判思路,流量告警分析技巧
  • SQL Server数据库多主模式解决方案
  • vue3标签中的ref属性如何使用$refs获取元素
  • 小菜家教平台(五):基于SpringBoot+Vue打造一站式学习管理系统
  • axios 访问data类型和.net core 后端对应接收
  • Ubuntu和Debian系列的Release默认shell解释器变更
  • 虚拟现实技术在旅游行业的应用
  • 42.第二阶段x86游戏实战2-lua寻找状态指针
  • C++设计模式结构型模式———责任链模式
  • mapreduce综合应用案例 — 气象数据清洗
  • StarTowerChain 与 DePIN:共筑区块链新蓝图
  • 洛谷每日一题——P1036 [NOIP2002 普及组] 选数、P1045 [NOIP2003 普及组] 麦森数(高精度快速幂)
  • 知从科技受邀出席ARM日产技术日
  • 智谱AI视频生成模型CogVideoX v1.5开源 支持5/10秒视频生成
  • Dear ImGui 使用VS2022编译为静态库
  • 信息安全工程师(84)UNIX/Linux操作系统安全分析与防护
  • 1.2 数据结构的分类与应用
  • AI 大模型:重塑软件开发的新力量
  • 新160个crackme - 095-tengxingCrackMe_v1.1
  • 界面控件DevExpress WPF中文教程:Data Grid——卡片视图设置
  • 初识Linux · 命名管道
  • 洛谷 P2239 [NOIP2014 普及组] 螺旋矩阵
  • lua 编译网路核心