• 多线程概述及创建


    什么是线程?

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

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

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

    多线程是什么?

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

    多线程的创建

    Thread类

    方式一:继承Thread类

    1、定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法。

    2、创建MyThread类的对象。

    3、调用线程对象的start()方法启动线程(启动后还是执行run方法的)。

    1. /**
    2. 目标:多线程的创建方式一:继承Thread类实现。
    3. */
    4. public class ThreadDemo1 {
    5. public static void main(String[] args) {
    6. // 3、new一个新线程对象
    7. Thread t = new MyThread();
    8. // 4、调用start方法启动线程(执行的还是run方法)
    9. t.start();
    10. for (int i = 0; i < 5; i++) {
    11. System.out.println("主线程执行输出:" + i);
    12. }
    13. }
    14. }
    15. /**
    16. 1、定义一个线程类继承Thread类
    17. */
    18. class MyThread extends Thread{
    19. public MyThread(){
    20. super();
    21. }
    22. /**
    23. 2、重写run方法,里面是定义线程以后要干啥
    24. */
    25. @Override
    26. public void run() {
    27. for (int i = 0; i < 5; i++) {
    28. System.out.println("子线程执行输出:" + i);
    29. }
    30. }
    31. }

     

    方式二:实现Runnable接口

    1、定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。

    2、创建MyRunnable任务对象。

    3、把MyRunnable任务对象交给Thread处理。

    4、调用线程对象的start()方法启动线程。

    1. **
    2. 目标:学会线程的创建方式二,理解它的优缺点。
    3. */
    4. public class ThreadDemo2 {
    5. public static void main(String[] args) {
    6. // 3、创建一个任务对象
    7. Runnable target = new MyRunnable();
    8. // 4、把任务对象交给Thread处理
    9. Thread t = new Thread(target);
    10. // Thread t = new Thread(target, "1号");
    11. // 5、启动线程
    12. t.start();
    13. for (int i = 0; i < 10; i++) {
    14. System.out.println("主线程执行输出:" + i);
    15. }
    16. }
    17. }
    18. /**
    19. 1、定义一个线程任务类 实现Runnable接口
    20. */
    21. class MyRunnable implements Runnable {
    22. /**
    23. 2、重写run方法,定义线程的执行任务的
    24. */
    25. @Override
    26. public void run() {
    27. for (int i = 0; i < 10; i++) {
    28. System.out.println("子线程执行输出:" + i);
    29. }
    30. }
    31. }

     也可以用匿名内部类形式

    1. /**
    2. 目标:学会线程的创建方式二(匿名内部类方式实现,语法形式)
    3. */
    4. public class ThreadDemo2Other {
    5. public static void main(String[] args) {
    6. Runnable target = new Runnable() {
    7. @Override
    8. public void run() {
    9. for (int i = 0; i < 10; i++) {
    10. System.out.println("子线程1执行输出:" + i);
    11. }
    12. }
    13. };
    14. Thread t = new Thread(target);
    15. t.start();
    16. new Thread(new Runnable() {
    17. @Override
    18. public void run() {
    19. for (int i = 0; i < 10; i++) {
    20. System.out.println("子线程2执行输出:" + i);
    21. }
    22. }
    23. }).start();
    24. new Thread(() -> {
    25. for (int i = 0; i < 10; i++) {
    26. System.out.println("子线程3执行输出:" + i);
    27. }
    28. }).start();
    29. for (int i = 0; i < 10; i++) {
    30. System.out.println("主线程执行输出:" + i);
    31. }
    32. }
    33. }

     

    方式三:利用Callable、FutureTask接口实现(JDK 5.0新增)

    1、得到任务对象

             定义类实现Callable接口,重写call方法,封装要做的事情。

             用FutureTask把Callable对象封装成线程任务对象。

    2、把线程任务对象交给Thread处理。

    3、调用Thread的start方法启动线程,执行任务 。

    4、线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果。

    FutureTask的API

     示例

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

    3种方式对比

    注意事项

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

    直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。 只有调用start方法才是启动一个新的线程执行,(看start源码)start方法会把线程加入线程组,每个线程都有机会交给cpu执行,这时线程的run方法会被自动调用,每个线程都有可能还没执行完就被中断从而让cpu执行其他线程,。

    2、为什么把主线程任务放在子线程之后?

    如果把主线程任务放在子线程之前,那么主线程一定是先跑完的,相当于是一个单线程的效果了。

  • 相关阅读:
    农林种植类VR虚拟仿真实验教学整体解决方案
    Python搭配GBase 8s
    Python-Django 项目模块-使用自定义管理应用-登录(十二)
    2023/9/20 -- C++/QT
    LINUX 用户和组操作
    Android问题笔记 - 关于SuperNotCalledException报错异常信息的解决方案
    预约挂号项目之预约挂号模块
    vr虚拟现实技术融入司法办案实操培训中的优势
    java中do、dto、vo、po的区别?
    window 安装多个低版本chrome测试
  • 原文地址:https://blog.csdn.net/daqi1983/article/details/134420010