Java技能树之“多线程”

举报
Mr.Z事顺意 发表于 2023/02/24 15:51:33 2023/02/24
【摘要】 一. 线程池概述1. 什么是线程池线程池和和字符串常量池, 数据库连接池一样, 都是为了提高程序的运行效率, 减少开销; 随着并发程度的提高, 当我们去频繁的创建和销毁线程, 此时程序的开销还是挺大的, 为了进一步提高效率, 就引入了线程池, 程序中所创建的线程都会加载到一个 “池子” 中, 当程序需要使用线程的时候, 可以直接从池里面获取, 用完了就将线程还给池, 这样在多线程的环境中就不...

一. 线程池概述
1. 什么是线程池

线程池和和字符串常量池, 数据库连接池一样, 都是为了提高程序的运行效率, 减少开销; 随着并发程度的提高, 当我们去频繁的创建和销毁线程, 此时程序的开销还是挺大的, 为了进一步提高效率, 就引入了线程池, 程序中所创建的线程都会加载到一个 “池子” 中, 当程序需要使用线程的时候, 可以直接从池里面获取, 用完了就将线程还给池, 这样在多线程的环境中就不用去重复的创建和销毁线程, 从而使程序的运行效率提高, 线程池是管理线程的方式之一.

🎯那为什么从线程池中“拿”线程会比直接创建线程要更加高效呢?

这是因为创建线程和销毁线程, 是交由操作系统内核完成的, 而我们使用线程池调度线程是在用户态实现的(用户代码中就能实现的,不必交给内核操作);

如果将任务交给内核态, 就需要通过系统调用, 让内核来执行任务, 但此时你不清楚内核身上背负着多少任务(内核不是只给一个应用程序服务, 是要给所有的程序都提供服务), 当使用系统调用, 执行内核代码的时候, 无法确定内核都要做哪些工作, 整体过程"不可控"的;

相比于内核来说, 用户态, 程序执行的行为是可控的, 用户态只去完成你所指定的任务, 效率更高, 开销更小.
2. Java标准库提供的线程池

Java中提供了线程池相关的标准类ThreadPoolExecutor, 也被称作多线程执行器, 该类中的线程包括两类, 一类是核心线程, 另一类是非核心线程, 当核心线程都被占用还不能满足程序任务执行的需求时, 就会启用非核心线程, 直到任务量少了, 随之非核心线程也就会销毁.

jdk8中提供了4个构造方法, 这里主要介绍和理解参数最多的那一个构造方法, 其他构造方法只是基于这里的减少了参数而已.

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

在Java标准库中提供了4个拒绝策略, 如下:
Modifier and Type    Class and Description
static class    ThreadPoolExecutor.AbortPolicy 如果任务太多, 队列满了, 直接抛出异常RejectedExecutionException .
static class    ThreadPoolExecutor.CallerRunsPolicy 如果任务太多, 队列满了, 多出来的任务, 谁加的, 谁负责执行.
static class    ThreadPoolExecutor.DiscardOldestPolicy 如果任务太多, 队列满了, 丢弃最旧的未处理的任务.
static class    ThreadPoolExecutor.DiscardPolicy 如果任务太多, 队列满了, 丢弃多出来的任务

多线程的实现方式(一)

  • 继承Thread类

    1、自定义一个类MyThread类,用来继承与Thread类

    2、在MyThread类中重写run()方法

    3、在测试类中创建MyThread类的对象

    4、启动线程

  • 代码如下:
    /**
     * @author 小贺.
     * @Description
     */
    public class Demo01 {
        public static void main(String[] args) {
            //创建线程
            MyThread t01 = new MyThread();
            MyThread t02 = new MyThread();
            MyThread t03 = new MyThread("线程03");
     
            //开启线程
    //        t01.run();
    //        t02.run();
    //        t03.run();
            // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
            // start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
            // 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
            // 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
            // run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
            t01.start();
            t02.start();
            t03.start();
            //设置线程名(补救的设置线程名的方式)
            t01.setName("线程01");
            t02.setName("线程02");
            //设置主线程名称
            Thread.currentThread().setName("主线程");
            for (int i = 0; i < 50; i++) {
                //Thread.currentThread() 获取当前正在执行线程的对象
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
    class MyThread extends Thread{
        public MyThread() {
        }
     
        public MyThread(String name) {
            super(name);
        }
     
        //run方法是每个线程运行过程中都必须执行的方法
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(this.getName() + ":" + i);
            }
        }
    }

此处最重要的为start()方法。单纯调用run()方法不会启动线程,不会分配新的分支栈。

start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。线程就启动成功了。

启动成功的线程会自动调用run方法(由JVM线程调度机制来运作的),并且run方法在分支栈的栈底部(压栈)。

run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

单纯使用run()方法是不能多线程并发的。

2.1 - 数据安全问题

    是否具备多线程的环境

    是否有共享数据

    是否有多条语句操作共享数据

    例如:我和小明同时取一个账户的钱,我取钱后数据还没返回给服务器,小明又取了,这个时候小明的余额还是原来的。

    如何解决?线程排队执行(不能并发),线程同步机制。

2.1.1 -变量对线程安全的影响

 实例变量:在堆中。

静态变量:在方法区。

局部变量:在栈中。

        以上三大变量中:
            局部变量永远都不会存在线程安全问题。
            因为局部变量不共享。(一个线程一个栈。)
            局部变量在栈中。所以局部变量永远都不会共享。

         实例变量在堆中,堆只有1个。
        静态变量在方法区中,方法区只有1个。
        堆和方法区都是多线程共享的,所以可能存在线程安全问题。

    局部变量+常量:不会有线程安全问题。
    成员变量:可能会有线程安全问题。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。