Java笔记之多线程(一)

news2024/12/24 7:42:41

文章目录

  • 前言
  • 一、什么是进程与线程?
    • 1.进程
    • 2.线程
    • 3.其他相关概念
  • 二、如何创建线程
    • 1.继承Thread类,重新run方法
    • 2.实现Runnable接口
    • 3.通过Callable和Future创建线程
    • 4. 继承Thread vs实现Runnable的区别
  • 三、用户线程和守护线程
    • 守护线程的使用
    • 设置成守护线程
  • 四、线程常用方法
    • 常用方法第一组
    • 常用方法第二组
  • 五、使用线程的好处
  • 六、线程生命周期


前言

在学习多线程之前,先了解什么是进程,什么是线程。


一、什么是进程与线程?

1.进程

  1. 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
  2. 进程是程序的一次执行过程,它有自己独立的生命周期,它会在启动程序时产生,运行程序时存在,关闭程序时消亡。

在这里插入图片描述

2.线程

1.线程由进程创建的,是进程的一个实体,是具体干活的人。
2.一个进程可以拥有多个线程。线程不独立分配内存,而是共享进程的内存资源,线程可以共享cpu的计算资源。
现在,进程更强调【内存资源的分配】,而线程更强调【计算资源的分配】。因为有了线程的概念,一个进程的线程就不能修改另一个线程的数据,隔离性更好,安全性更好。
我们在使用浏览器播放视频或者下载文件时,我们可以同时播放或者下载多个,那播放两个视频或者下载多个文件就是不同线程在做的工作,否则,你一定是需要等待一个结束了,另一个才能开始。
在这里插入图片描述我们可以在计算机的任务管理器中查看当前计算机的进程和线程数。
在这里插入图片描述

3.其他相关概念

1.单线程:同个时刻,只允许执行一个线程
2.多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个浏览器进程,可以同时下载多个文件
3.并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简
单的说,单核cpu实现的多任务就是并发。
在这里插入图片描述
4.并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
在这里插入图片描述

二、如何创建线程

创建线程主要有三种方式:

1.继承Thread类,重新run方法

步骤:

• 定义类继承Thread;
• 重写Thread类中的run方法;(目的:将自定义代码存储在run方法,让线程运行)
• 调用线程的start方法:(该方法有两步:启动线程,调用run方法)

代码如下(示例):

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        new ThreadTest().start();
        while (true) {
            System.out.println(Thread.currentThread().getName()+"---1");
            Thread.sleep(1000);
        }
    }
}
class ThreadTest extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName()+"---2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

在这里插入图片描述

2.实现Runnable接口

步骤:

• 创建任务: 创建类实现Runnable接口
• 使用Thread 为这个任务分配线程
• 调用线程的start方法

代码如下(示例):

public class RunnableDemo {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 11; i++) {
            Thread.sleep(1000);
            System.out.println("hi"+i);
            if (i == 5) {
                T3 t3 = new T3();
                Thread thread = new Thread(t3);
                thread.start();
                System.out.println("子线程先开始");
                thread.join();
                thread.setDaemon(true);
                System.out.println("子线程结束,主线程开始");
            }
        }
    }
}
class T3 implements Runnable {
    @Override
    public void run() {
        int sum = 0;
        while (true){
            try {
                Thread.sleep(1000);
                sum++;
                System.out.println("hello"+sum +"次");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (sum == 10){
                break;
            }
        }
    }
}

3.通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

代码如下(示例):

public class UseCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println(1);
        //保存一个将来的返回值
        FutureTask<String> futureTask = new FutureTask<>(new Task());
        new Thread(futureTask).start();
        System.out.println(3);
        System.out.println(4);
        String result = futureTask.get();
        System.out.println(5);
        System.out.println(result);
        System.out.println(6);
    }
    static class Task implements Callable<String> {

        public String call() throws Exception {
            while (true) {
                Thread.sleep(1000);
                return Thread.currentThread().getName() + "---2";
            }
        }
    }
}

注:futureTask.get();这是一个阻塞的方法,意思就是,这个方法会一直等,主线程会一直等待,这个线程执行完成之后并有了返回值,才会继续执行。

4. 继承Thread vs实现Runnable的区别

  1. 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本
    质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了 单继承的限制

三、用户线程和守护线程

Java提供两种类型的线程:用户线程和守护程序线程

1. 用户线程: 也叫工作线程,当线程的任务执行完或通知方式结束。
2. 守护线程: 一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
3.常见的守护线程: 垃圾回收机制

守护线程的使用

守护线程对于后台支持任务非常有用,例如垃圾收集,释放未使用对象的内存以及从缓存中删除不需要的条目。大多数JVM线程都是守护线程。在比如qq等等聊天软件,主程序是非守护线程,而所有的聊天窗口是守护线程,当在聊天的过程中,直接关闭聊天应用程序时,聊天窗口也会随之关。包括word中我们在书写文字的时候,还有线程帮我们进行拼写检查,这都是守护线程。

设置成守护线程

要将线程设置为守护线程,我们需要做的就是调用Thread.setDaemon()

代码示例:

thread.setDaemon(true);

任何线程都继承创建它的线程的守护进程状态。由于主线程是用户线程,因此在main方法内创建的任何线程默认为用户线程。

四、线程常用方法

常用方法第一组

  1. setName //设置线程名称,使之与参数name相同
  2. getName //返回该线程的名称
  3. start //使该线程开始执行;Java 虚拟机底层调用该线程的start0方法
  4. run //调用线程对象 run方法;
  5. setPriority //更改线程的优先级
  6. getPriority //获取线程的优先级
  7. sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  8. interrupt //中断线程

注意事项和细节

  1. start底层会创建新的线程,调用run,run 就是一个简单的方法调用,不会启动新线程
  2. 线程优先级的范围
    /**
     * 线程的最小优先级 .
     */
    public static final int MIN_PRIORITY = 1;

    /**
     * 线程的默认优先级 .
     */
    public static final int NORM_PRIORITY = 5;

    /**
     * 线程的最大优先级 .
     */
    public static final int MAX_PRIORITY = 10;
  1. interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
  2. sleep:线程的静态方法,使当前线程休眠

常用方法第二组

  1. yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
  2. join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

示例代码:

public class Thread03 {
    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        t1.start();
        for (int i = 0; i < 11; i++) {
            System.out.println("主线程(小弟)吃" + i + "包子" );
            if (i == 5) {
                System.out.println("主线程(小弟)让(老大)先吃");
                t1.join();
                //Thread.yield();
                System.out.println("主线程(老大)吃完了,小弟吃");
            }
        }
    }
}
class T2 implements Runnable {
        @Override
        public void run() {
            int sum = 0;
            while (true) {
                try {
                    Thread.sleep(1000);
                    sum++;
                    System.out.println("hello" + sum + "次");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (sum == 10) {
                    break;
                }
            }
        }
    }

五、使用线程的好处

1.加速,提高CPU的使用率
2.能够异步无干扰进行其他业务操作

代码示例:

public class UseCallable1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //单线程计算
        long start = System.currentTimeMillis();
        long res = 0L;
        for (int i = 0; i < 1000000000; i++) {
            res += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("---单线程计算时间" + (end - start));
        System.out.println(res);
        //多线程计算
        start = System.currentTimeMillis();
        FutureTask[] futureTasks = new FutureTask[10];
        res = 0L;
        for (int i = 0; i < 10; i++) {
            FutureTask<Long> futureTask = new FutureTask<>(new Task(i*100000000,(i+1)*100000000));
            new Thread(futureTask).start();
            futureTasks[i] = futureTask;
        }
        for (int i = 0; i < futureTasks.length; i++) {
            long sum = (long) futureTasks[i].get();
            res += sum;
        }
        end = System.currentTimeMillis();
        System.out.println("---多线程计算时间" + (end - start));
        System.out.println(res);
    }

    static class Task implements Callable<Long> {

        private int from;
        private int to;
        public Task(int from, int to) {
            this.from = from;
            this.to = to;
        }

        @Override
        public Long call() throws Exception {
            long sum = 0L;
            for (int i = from; i < to; i++) {
                sum += i;
            }
            return sum;
        }
    }
}

六、线程生命周期

生命周期可以通俗地理解为“从摇篮到坟墓”(Cradle-to-Grave)的整个过程。线程的生命周期包括从创建到终结的整个过程。
JDK中用Thread.State枚举表示了线程的几种状态

    public enum State {
        /**
         * 尚未启动的线程处于此状态.
         */
        NEW,

        /**
         * 在Java虚拟机中执行的线程处于此状态.
         */
        RUNNABLE,

        /**
         * 被阻塞等待监视器锁定的线程处于此线程.
         */
        BLOCKED,

        /**
         * 正在等待另一个线程执行特定动作的线程处于此状态.
         */
        WAITING,

        /**
         * 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
         */
        TIMED_WAITING,

        /**
         * 己退出的线程处于此状态.
         */
        TERMINATED;
    }

其中:RUNNABLE中有ReadyRunning两种状态
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python百日进阶-数据分析】Day137 - plotly旭日图:go.sunburst()实例

文章目录4.2 带有 go.Sunburst 的基本旭日图4.2.1 基本go.sunburst()旭日图4.2.2 带有重复标签的旭日图4.2.3 分支值4.2.4 大量切片4.2.5 控制旭日形扇区内的文本方向4.2.6 使用 uniformtext 控制文本字体大小4.2.7 具有连续色标的旭日图4.2.8 Dash中的go.sunburst()4.2 带有 g…

Android hilt 依赖注入使用详解

文章目录官方文档添加依赖初始化hiltMainActivity 使用共享类在 MainActivity 添加依赖注入ActivityScoped 作用域Singleton 作用域构造参数&#xff0c;添加 Context参数ApplicationContext、ActivityContext官方文档 https://developer.android.com/training/dependency-inj…

【Linux】缓冲区/磁盘inode/动静态库制作

目录 一、缓冲区 1、缓冲区的概念 2、缓冲区的意义 3、缓冲区刷新策略 4、同一份代码&#xff0c;打印结果不同 5、仿写FILE 5.1myFILE.h 5.2myFILE.c 5.3main.c 6、内核缓冲区 二、了解磁盘 1、磁盘的物理结构 2、磁盘的存储结构 2.1磁盘的定位 3、磁盘的抽象…

基于价值迭代求解迷宫寻路问题

摘 要 迷宫寻路是人工智能和计算机科学中一个经典的问题。它涉及在迷宫中找到一条从起点到终点的最短路径。这个问题可以用来模拟真实世界中的许多情况&#xff0c;例如机器人在工厂中自动导航&#xff0c;搜索引擎在网络中寻找信息&#xff0c;或者人类在城市中导航。 迷宫寻路…

【Javascript基础】--零基础--超详细且简洁的Javascript笔记--简介(01)

参考资料&#xff1a; 【现代Javascript教程】https://zh.javascript.info/ 【MDN】https://developer.mozilla.org/zh-CN/ 笔记仅作为学习交流载体&#xff0c;无任何商业或盈利目的 JavaScript 简介 了解 JavaScript 有什么特别之处&#xff0c;我们可以用它实现什么&#…

适合编程初学者的开源博客系统(Vue3+Element Plus版)

目标 为编程初学者打造入门学习项目&#xff0c;使用各种主流编程语言来实现。让想学编程的&#xff0c;一个都不落下。 上述基本涵盖了当前编程开发所有主流语言。 左侧为前端版本&#xff1a;安卓、iOS、鸿蒙、Flutter、Vue、uni-app、微信小程序。 右侧为服务器端版本&am…

YOLOV7学习记录之模型推理

前面我们学习了YOLOV7的训练过程&#xff0c;今天我们学习其推理过程&#xff0c;即模型预测&#xff1a;其包含损失函数计算&#xff0c;输出值解码&#xff0c;非极大值抑制&#xff0c;mAP计算等过程。 同时还介绍原始图像上绘制目标框等功能。 我们从predict.py文件开始&am…

【源码共读】Vite 项目自动添加 eslint 和 prettier

vite-pretty-lint库是一个为Vite创建的Vue或React项目初始化eslint和prettier的库。 该库的目的是为了让开发者在创建项目时&#xff0c;不需要手动配置eslint和prettier&#xff0c;而是通过vite-pretty-lint库来自动配置。 源码地址&#xff1a; vite-pretty-lintgithub1s…

设计模式 - 单例模式(一)

单例模式一 官方定义二 单例模式八种方式2.1 饿汉式&#xff08;静态常量&#xff09;代码案例案例分析2.2 饿汉式&#xff08;静态代码块&#xff09;代码案例案例分析2.3 懒汉式(线程不安全)代码案例案例分析2.4 懒汉式(线程安全&#xff0c;同步方法)代码案例案例分析2.5 懒…

数据要求说明书(GB856T——88)基于协同的在线表格forture-sheet

数据要求说明书 1引言 1.1编写目的 本份数据要求说明书详细的提供了系统中各个数据的流向&#xff0c;是设计数据库的关键所在。为以后的编码以及测试提供一份可靠的依据。 预期的读者&#xff1a;系统开发人员、系统测试人员、系统维护人员 1.2背景 待开发的数据库名称&a…

揭秘百度智能测试在测试定位领域的实践

以前&#xff0c;我们介绍了测试活动测试输入、测试执行、测试分析、测试定位和测试评估五个步骤中测试输入、执行、分析、评估的智能化研究和实践&#xff0c;本文重点介绍测试定位环节的智能化实践。 测试定位的主要作用是在构建失败或问题发生后&#xff0c;快速给出产生该现…

机器学习之回归

回归算法 线性回归 求解线性回归方法 正规方程梯度下降 迭代 API sklearn.linear_model.LinearRegression 正规方程优化fit_intercept 是否计算偏置量&#xff0c;没有的化经过原点属性 coef_ 回归系数intercept_ 偏置量 sklearn.linear_model.SGDRegressor 使用随机梯度…

转行了!文科生转程序员的外包工作经历分享

01 种子 我是一名文科生&#xff0c;法律专业&#xff0c;武汉某 211 &#xff0c;入这行纯属巧合。 大三下半年&#xff0c;大家纷纷准备秋招&#xff0c;我去校园招聘会上溜达了一圈&#xff0c;好奇而去&#xff0c;丧气而归。 或许是因为大学三年过得太过安逸(宅在宿舍打…

C#语言实例源码系列-实现本地磁盘目录

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

POMO: Policy Optimization with Multiple Optima for Reinforcement Learning学习笔记

文章目录摘要零、一些基础1.梯度近似2.策略梯度定理3.REINFORCE4.REINFORCE with Baseline5.REINFORCE Actor-Critic一、介绍二、相关工作&#xff08;1&#xff09;深度强化学习构建法&#xff08;2&#xff09;推理技术&#xff08;3&#xff09;深度强化学习改善法三、激励四…

制作 iOS 证书

准备工作 您需要 注册并加入 Apple Developer Program&#xff0c;才能在 App Store 上架应用。请准备一台 macOS 系统的电脑&#xff0c;用于证书制作。 创建 App ID 登录 Apple Developer&#xff0c;选择 Certificates, Identifiers & Profiles 选项。 选择 Identifi…

自学编程和计算机科班出身的差别在哪里

前不久逛知乎的时候看到一个问题&#xff1a;自学编程和计算机科班出身的差别在哪里&#xff1f; 自己回答了一下&#xff0c;获得了比较多的点赞和评论&#xff0c;在这里也分享给大家。 985 通信专业学长&#xff0c;转行程序员&#xff0c;聊一聊我的看法&#xff1a;说一千…

k8s之Job 与 CronJob

写在前面 语雀原文阅读效果更佳&#xff1a; 5、Job与CronJob 语雀 《5、Job与CronJob》 Job 与 CronJob 接下来给大家介绍另外一类资源对象&#xff1a;Job&#xff0c;我们在日常的工作中经常都会遇到一些需要进行批量数据处理和分析的需求&#xff0c;当然也会有按时间来…

RK3568平台开发系列讲解(工具命令篇)常用 GIT 命令汇总

🚀返回专栏总目录 文章目录 一、GIT BASICS二、GIT DIFF三、UNDOING CHANGES四、REWRITING GIT HISTORY五、GIT BRANCHES六、REMOTE REPOSITORIES七、GIT CONFIG八、GIT LOG九、GIT RESET十、GIT PULL十一、GIT PUSH沉淀、分享、成长,让自己和他人都能有所收获!😄 一、GI…

基于51单片机的酒精气体检测器设计

使用说明&#xff1a; 上电以后&#xff0c;需要预热一段时间&#xff0c;此时lcd显示Loading... &#xff08;预热过程为电压先上升后下降的改成&#xff0c;通过检测电压来检测是否预热完成&#xff09; &#xff0c; 预热完成后显示酒精浓度&#xff0c;按下按下按键key1即…