首页 > 其他分享 >多线程1:线程的创建方式

多线程1:线程的创建方式

时间:2024-11-17 19:14:03浏览次数:3  
标签:输出 Thread 创建 主线 start 线程 new 多线程

欢迎来到“雪碧聊技术”CSDN博客!

在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。

让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!

目录

一、什么是多线程?

1、什么是线程?

2、什么是多线程?

3、多线程用在哪里?有什么用?

举例1:12306购票

举例2:百度网盘上传、下载同时完成

二、线程的创建方式1-继承Thread类

1、步骤

①创建一个类,继承Thread类,代表该类是一个线程类

②重写Thread类的run方法

③创建线程类的一个对象,代表一个新线程

④调用该对象的start方法,启动该线程

2、举例

3、注意事项

①启动线程必须是调用start方法,而不是调用run方法

②不要把主线程任务放在启动子线程之前

4、优缺点

三、线程的创建方式2-实现Runnable接口

1、步骤

①定义一个线程任务类,实现Runnable接口,并重写run方法

②创建MyRunnable任务对象

③把任务对象交给Thread类的构造器,将其封装为一个线程对象

④调用线程对象的start方法启动线程。

2、举例

3、优缺点

4、简化写法:匿名内部类写法

代码解释:

四、线程的创建方式3-实现Callable接口

1、前两种创建线程方式存在的问题

2、怎么解决这个问题?

3、步骤

4、执行流程

5、优缺点

五、三种创建线程方式的总结

结语


一、什么是多线程?

1、什么是线程?

        线程(Thread)是一个程序内部的一条执行流程。

        举例:

2、什么是多线程?

        多线程:指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。

3、多线程用在哪里?有什么用?

举例1:12306购票

        网上买票的时候,肯定不止你自己,而是成千上万的人同时在购票,此时就需要12306的服务器提供很多的执行流程(即,很多线程)来进行购票业务的执行。

举例2:百度网盘上传、下载同时完成

一个线程负责下载,一个线程负责上传。

二、线程的创建方式1-继承Thread类

1、步骤

①创建一个类,继承Thread类,代表该类是一个线程类

②重写Thread类的run方法

③创建线程类的一个对象,代表一个新线程

④调用该对象的start方法,启动该线程

2、举例

public class test1 {
    public static void main(String[] args) {
        //目标:掌握线程的创建方式一:继承Thread类
        //4、创建线程类的对象,一个对象就是一个线程
        MyThread t1 = new MyThread();
        //5、调用对象的start方法,启动线程
        t1.start();

        //主线程(main函数所在的那条执行流程)代码
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:"+i);
        }
    }
}

//1、定义一个子类继承Thread类,成为一个线程类
class MyThread extends Thread{
    //2、重写Thread类的run方法
    public void run() {
        //3、在run方法中编写线程的任务代码(线程要干的活)
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出:"+i);
        }
    }
}

运行结果:

主线程输出:0
主线程输出:1
子线程输出:0
子线程输出:1
子线程输出:2
子线程输出:3
子线程输出:4
主线程输出:2
主线程输出:3
主线程输出:4

可见此时主线程和子线程是并发执行的关系,输出的for循环都是交叉的,因此这就是多线程。

3、注意事项

①启动线程必须是调用start方法,而不是调用run方法

简单来说:只有通过start方法,才能启动一个新的线程。而run方法不行。

  • 直接调用run方法,会把该方法当成一个普通方法执行,此时相当于单线程。
  • 只有调用start方法才是启动一个新的线程执行。

        举例:

        运行效果:

②不要把主线程任务放在启动子线程之前

  • 这样主线程的任务先跑完,再启动子线程,那效果还是单线程的效果。

        举例:

        运行效果:

4、优缺点

  • 优点:编码简单
  • 缺点:存在单继承的局限性,线程类继承Thread后,不能再继承其他类,不便于功能扩展。

三、线程的创建方式2-实现Runnable接口

1、步骤

①定义一个线程任务类,实现Runnable接口,并重写run方法

②创建MyRunnable任务对象

③把任务对象交给Thread类的构造器,将其封装为一个线程对象

④调用线程对象的start方法启动线程。

2、举例

public class test2 {
    public static void main(String[] args) {
        //目标:掌握线程的创建方式2:实现Runnable接口

        //2、创建MyRunnable任务对象
        MyRunnable r = new MyRunnable();
        //3、把任务对象交给Thread类的构造器,将其封装为一个线程对象
        Thread t1 = new Thread(r);
        //4、调用线程对象的start方法启动线程。
        t1.start();

        //主线程的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:"+i);
        }
    }
}

//1、定义一个线程任务类,实现Runnable接口,并重写run方法
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出:"+i);
        }
    }
}

运行效果:


主线程输出:0
主线程输出:1
子线程输出:0
主线程输出:2
主线程输出:3
子线程输出:1
主线程输出:4
子线程输出:2
子线程输出:3
子线程输出:4

可见此时主线程、子线程交替输出,是并发的。

3、优缺点

  • 优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
  • 缺点:需要多一个实现Runnable接口的步骤

4、简化写法:匿名内部类写法

public class test3 {
    public static void main(String[] args) {
        //掌握:匿名内部类写法
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程输出:"+i);
                }
            }
        }).start();

        //主线程任务
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:"+i);
        }
    }
}

运行效果:

子线程输出:0
主线程输出:0
主线程输出:1
主线程输出:2
主线程输出:3
子线程输出:1
主线程输出:4
子线程输出:2
子线程输出:3
子线程输出:4

可见此时主线程和子线程还是交替输出的,是并发关系。

代码解释:

因此上述代码就可以理解为:

        new Thread(任务类对象).start();

因此此时Thread类的构造器,会将任务类对象封装成一个线程对象,此时在调用该线程对象的.start方法,即可启动该线程。

四、线程的创建方式3-实现Callable接口

1、前两种创建线程方式存在的问题

        线程执行完毕后有一些数据需要返回,他们重写的run方法均不能返回结果。

2、怎么解决这个问题?

        JDK 5.Callable接口和FutureTast类来实现。

3、步骤

public class Test4 {
    public static void main(String[] args) {
        //目标:掌握多线程的创建方式三:实现Callable接口。
        //方式三的优势是:可以获取线程执行完毕后的结果
        //3、创建一个实现Callable接口的实现类对象
        Callable<String> c1 = new MyCallable(100);//多态的写法
        //4、把Callable对象封装成一个真正的线程任务对象FutureTask对象
        /**
         * 未来任务对象的作用:
         *      ①本质是一个Runnable线程任务对象,可以交给Thread线程对象处理
         *      ②可以获取线程执行完毕后的结果
         */
        FutureTask<String> f1 = new FutureTask<>(c1);
        //5、把FutureTask对象作为参数传递给Thread线程对象
        Thread t1 = new Thread(f1);
        //6、启动线程
        t1.start();



        Callable<String> c2 = new MyCallable(50);//多态的写法
        FutureTask<String> f2 = new FutureTask<>(c2);
        Thread t2 = new Thread(f2);
        t2.start();


        //获取线程执行完毕后的结果
        try {
            //如果主线程发现第一个线程还没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行!
            //这样才能保证拿到的结果是准确的,防止了线程运行一半,就将结果获取。
            System.out.println(f1.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        try {
            //如果主线程发现第二个线程还没有执行完毕,会让出CPU,等第二个线程执行完毕后,才会往下执行!
            //这样才能保证拿到的结果是准确的,防止了线程运行一半,就将结果获取。
            System.out.println(f2.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

    }
}

//1、定义一个实现Callable接口的实现类
class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n){
        this.n = n;
    }

    //2、重写call方法,定义线程执行体
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum+=i;
        }
        return "子线程1-"+n+"的和是:"+sum;
    }
}

4、执行流程

  • 线程执行体,是Callable接口的call方法;
  • 获取线程运行的结果,靠FutureTask类的get()方法;
  • 启动线程,是靠Thread类的start()方法。

5、优缺点

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

五、三种创建线程方式的总结

结语

以上就是多线程的三种创建方式,想了解更多的多线程知识,请关注博主的本专栏文章~~

标签:输出,Thread,创建,主线,start,线程,new,多线程
From: https://blog.csdn.net/qq_63981644/article/details/143778160

相关文章

  • 在 SQL Server 中,创建表时可以直接为字段添加唯一约束(UNIQUE)
    在SQLServer中,创建表时可以直接为字段添加唯一约束(UNIQUE)。在CREATETABLE语句中,定义字段时,可以使用UNIQUE关键字来确保该字段的值唯一。语法:sqlCREATETABLEyour_table(aINTUNIQUE,--其他字段);假设你需要创建一个表employees,其中字段email需要保......
  • Unix线程
    文章目录线程与进程线程优缺线程数据结构线程ID线程创建线程安全函数线程终止线程等待与分离线程终止处理程序线程同步互斥量互斥量原理读写锁条件变量屏障线程与信号信号接收线程与fork线程IO线程与进程线程是CPU调度的基本单位;进程是资源分配的基本单位,访问......
  • 线程到底设置数量多少合适!
    可能很多人都看到过一个线程数设置的理论:CPU密集型的程序-核心数+1I/O密集型的程序-核心数*2不会吧,不会吧,真的有人按照这个理论规划线程数?线程数和CPU利用率的小测试抛开一些操作系统,计算机原理不谈,说一个基本的理论(不用纠结是否严谨,只为好理解):一个CPU核心,单位......
  • Java面试之多线程&并发篇(5)
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!常用的线程池有哪些?简述一下你对线程池的理解?Java程序是如何执行的?锁的优化机制了解吗?说说进程和线程的区别?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12万字的java面试题整理******java核心面试知......
  • @Transactional事务注解与函数内多线程并发编程出现的问题
    @Transactional当@Transactional注解写在函数上之后,就表示这个函数开启了事务。事务是基于数据库连接的connect。parallelStream这是针对List进行多线程Stream的操作。//对list集合开启多线程操作list.parallelStream().forEach(item->{//业务代码})@Transactional和pa......
  • Java 中常见的三类线程安全问题:解决方案与实例分析
    在Java并发编程中,线程安全是一个非常重要的概念。如果多个线程同时访问一个共享资源而不进行适当的同步,就会出现线程安全问题,导致程序行为异常。根据不同的场景,线程安全问题可以分为运行结果错误、发布和初始化导致的线程安全问题和活跃性问题。本文将详细探讨这三类线程......
  • Cocos2d-x渲染系统多线程优化原理探索
    Cocos2d-x的渲染系统通过深度优先遍历场景树来实现高效的渲染,而将渲染过程进行Job化是提升性能的有效策略。你提到的两种策略都具有一定的合理性,但也存在各自的局限性。以下是对这两种策略的详细分析,以及如何进一步优化整个渲染过程的建议。策略1:Job化RenderCommand生成方......
  • HbuilderX 插件开发-模板创建
    实现思路使用HbuilderX打开某个文档时右键点击的时候获取当前打开的文档内容使用API替换为自己的模板示例package.json{ "id":"SL-HbuilderX-Tool", "name":"SL-HbuilderX-Tool", "description":"快速创建html,vue2模板", "displayName":......
  • 进程、线程、协程
    进程、线程、协程文章目录进程、线程、协程一、进程的出现二、线程的出现三、协程协程与函数的区别协程如何实现?**协程与线程的区别**多个执行流但只有一个线程为什么要使用协程?一、进程的出现CPU是不知道进程、线程的概念的,CPU只知道做两件事情。从内存中读取指......
  • 多线程进阶
    1.常见的锁策略如果你自己实现一把锁,你认为标准库给你提供的锁不够用,这个时候你就需要关注锁策略,其实synchronized已经非常好用了足够覆盖大多数的使用场景。这里的锁策略不是和java强相关的,其他语言但凡涉及到并发编程,设计到锁都可以谈到这样的锁策略。1.1乐观锁VS悲观锁......