线程和进程你真的了解吗?快来看看全面的解析和线程的代码

news2025/1/21 6:24:48

(本篇文章全面的释义了线程和进程;为了方便大家熟练的掌握,内容中引入了详细的代码;欢迎大家学习讨论和批评指正)

进程的概念

操作系统(OS)中并发(同时)执行的多个程序任务

进程的特点

  • 宏观并行,微观串行

在一个时间段内,CPU会将时间段划分为若干个时间片,一个时间片是能被一个程序拥有,且只有拥有时间片的程序才能执行自身内容,所以当时间片的划分足够细小,交替频率足够快,就会形成并行的假象,时间上仍然是串行.

线程的概念

  • 是进程的基本组成部分

是进程中并发执行的多个任务

线程的特点

  • 宏观并行,微观串行

一个时间片只能被一个进程拥有,一个进程一次又只能执行一个线程. 由于进程之间交替执行,所以线程之间必定也是交替执行

多线程

  • 只存在多线程,不存在多进程

正在执行中的程序才叫进程,其他的都是等待执行的程序

无论是否拥有时间片,线程任务都叫线程

线程执行的组成

  1. 时间片

    • CPU调度分配, 线程争抢拥有
  2. 数据

    • 堆: 堆共享
    • 栈: 栈独立
  3. 代码

    • 书写逻辑

线程的创建

  1. 继承Thread, 重写run方法

    public class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 1; i <=100 ; i++) {
                System.out.println(i);
            }
        }
        
    }
    
    package com.by.test;
    
    import com.by.thread.MyThread;
    
    public class Test1 {
        public static void main(String[] args) {
            Thread t1 = new MyThread();
            Thread t2 = new MyThread();
    
            t1.start();
            t2.start();
            /*t1.run();
            t2.run();*/
    
            System.out.println("main结束");
        }
    }
    
  2. 实现Runnable,重写run方法. 在Thread对象的构造中传入任务对象

    package com.by.dao.impl;
    
    /**
     * 线程任务
     */
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i=1;i<=100;i++) {
                System.out.println(i);
            }
        }
    }
    
    package com.by.test;
    
    import com.by.dao.impl.MyRunnable;
    
    import javax.print.attribute.standard.RequestingUserName;
    
    public class Test2 {
        public static void main(String[] args) {
            /*//先创建任务对象
            Runnable r = new MyRunnable();
            //将任务对象传入线程对象
            Thread t1 = new Thread(r);*/
    	//任务只会执行一次时,可以通过匿名内部类或者lambda简化书写
           Thread t1=new Thread(new Runnable() {
               @Override
               public void run() {
                   for (int i = 1; i <=100 ; i++) {
                       System.out.println("t1:: "+i);
                   }
               }
           });
    
           Thread t2=new Thread(()->{
                   for (int i = 101; i <=200 ; i++) {
                   System.out.println("t2> "+i);
               }
           });
    
            t1.start();
           t2.start();
    
    
        }
    }
    

更推荐使用第二种创建方式: 更符合类的单一职责,将线程对象的创建与线程任务的书写分离,更有利于后期的维护

使用

  1. 当开启多个线程之后, 线程之间会争抢时间片,拿到时间片的线程执行自身内容,其他线程无法执行,只能继续尝试争抢时间片,直到线程内容执行结束,才会脱离争夺队列
  2. 主函数也成为主线程,其一定是首个拥有时间片的线程
  3. 当开启多个线程之后,JVM执行结束的标志将从主函数执行结束转换为所有线程执行结束
  4. 开启线程需要调用线程对象.start()方法

线程状态

基础状态

在这里插入图片描述

等待状态

  • 也称为阻塞状态
  1. sleep()

    • Thread.sleep(毫秒数): 使当前线程释放自身时间片, 进入有限期休眠状态,在休眠时间内,该线程无法争抢时间片,休眠结束之后,才可以进入到就绪状态
      • 1秒=1000毫秒
    • 该方法需要处理非运行时异常, run方法不可上抛异常,所以必须通过try-catch处理解决
    Thread t1=new Thread(new Runnable() {
               @Override
               public void run() {
                   //让当前线程休眠3秒钟
                   try {
                       Thread.sleep(3000);
                   } catch (InterruptedException e) {
                       System.out.println("休眠异常");
                   }
                   for (int i = 1; i <=100 ; i++) {
                       System.out.println("t1:: "+i);
                   }
               }
           });
    
  2. join()

    • 线程对象.join(): 使调用者线程在当前线程之前执行, 当前线程只有等调用者线程执行结束进入死亡状态之后才有可能回到就绪状态.
    • 该方法需要处理非运行时异常,run方法无法上抛,必须通过try-catch处理
    package com.by.test;
    
    public class Test3 {
        public static void main(String[] args) {
            //以下代码执行顺序:t1->t2->t3
            Thread t1=new Thread(()->{
                for (int i = 0; i < 30; i++) {
                    System.out.println("t1:  "+i);
                }
            });
    
            Thread t2=new Thread(()->{
                //使t1线程在t2线程之前执行
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    System.out.println("join失败");
                }
                for (int i = 101; i < 130; i++) {
                    System.out.println("t2>"+i);
                }
            });
    
            Thread t3=new Thread(()->{
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    System.out.println("join失败");
                }
                for (char i = 65; i <=90; i++) {
                    System.out.println("t3::"+i);
                }
            });
    
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

sleep和join的区别?

  1. sleep方法进入的是有限期等待状态,join方法进入的是无限期等待状态
  2. sleep是静态方法,可以直接通过类名调用,join是非静态方法,必须通过线程对象调用

线程池

前言: 当一个任务需要多次执行时,如果将任务放置于线程对象Thread中,会浪费内存空间导致不合理的并发,线程池可以解决该问题

作用

管理盛放线程任务, 将需要执行的任务提交执行,任务结束之后池与任务并不会立即销毁,任务对象会回到池中等待下次执行,直到线程池关闭,内部任务才会失效

创建-API

  1. ExecutorService: 线程池接口

    • submit(线程任务对象): 提交线程任务使其执行
    • shutdown(): 关闭线程池
  2. Executors: 线程池工具类,用来获取线程池对象

    • newCachedThreadPool(): 获取一个不固定并发数量的线程池对象
    • newFixedThreadPool(int ):获取一个固定并发数量的线程池对象

    不固定并发数量的线程池: 所有提交到池中的任务都会同时并发

    固定并发数量的线程池: 对应并发数量的任务先并发执行,超出的任务需要等待执行,等池中执行的任务结束让位之后,超出部分的任务才会进入池中执行

线程任务

  1. Runnable: run()

    • 无返回值,不能上抛异常
    package com.by.test;
    
    import com.by.dao.impl.MyRunnable;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test4 {
        public static void main(String[] args) {
            //获取一个不固定并发数量的线程池
           // ExecutorService es1 = Executors.newCachedThreadPool();
            ExecutorService es1 = Executors.newFixedThreadPool(2);
    
            Runnable r1=new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 50; i++) {
                        System.out.println("r1>>"+i);
                    }
                }
            };
            Runnable r2=new Runnable() {
                @Override
                public void run() {
                    for (int i = 50; i < 100; i++) {
                        System.out.println("r2:::"+i);
                    }
                }
            };
            //提交任务执行
            es1.submit(r1);
            es1.submit(r2);
            es1.submit(r2);
    
            //关闭线程池
            es1.shutdown();
        }
    }
    
  2. Callable: call()

    • 有返回值,可以上抛异常,默认上抛Exception
    • 返回值会存放于一个Future对象中,可以通过Future对象.get()获取内部的返回值
    Callable<返回值类型> c1=new Callable<返回值类型>() {
                @Override
                public 返回值类型 call() throws Exception {
                   //...
                    return;
                }
            };
    
    package com.by.test;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Test5 {
        public static void main(String[] args) throws Exception {
            //创建线程池
            ExecutorService es = Executors.newCachedThreadPool();
            //线程任务1: 计算1-100的和并返回
            Callable<Integer> c1=new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int i = 0; i < 101; i++) {
                        sum += i;
                    }
                    return sum;
    
                }
            };
            //提交任务执行并接收
            Future<Integer> f =es.submit(c1);
            System.out.println(f.get());
    
            System.out.println(es.submit(c1).get());
    
            //关闭线程池
            es.shutdown();
        }
    }
    

线程安全问题

  • 当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题
    • 临界资源: 被多个线程同时访问的对象
    • 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作

互斥锁

  • 每个对象都默认拥有互斥锁, 该锁默认不开启.
  • 当开启互斥锁之后,线程想要访问对象,则在需要拥有时间片的基础上也拥有锁标记,锁标记只能被一个线程拥有,拥有时间片和锁标记的线程才能执行自身内容,在此期间,其他线程只能等正在执行的线程执行结束释放锁标记和时间片之后才能进入就绪状态
  • synchronized: 开启互斥锁的关键字
同步方法
  • 思路: 在被线程同时访问的方法上加锁

    访问修饰符 synchronized 返回值类型 方法名(参数列表){
        
    }
    
    package com.by.util;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 工具类-操作集合属性
     */
    public class MyList {
        private List<Integer> list = new ArrayList<>();
    
        /**
         * 给集合属性添加元素
         * @param n 添加的元素值
         synchronized: 同步方法
         */
        public synchronized void insert(int n){
            list.add(n);
        }
        /**
         * 查看集合内容
         */
        public void query(){
            System.out.println("集合长度: " + list.size());
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+"  ");
            }
        }
    
    }
    
同步代码块
  • 思路: 让参与临界资源对象访问的线程自身加锁

    synchronized(临界资源对象){
        //需要被认定为原子操作的代码
    }
    
  • 使用: 所有访问同一临界资源的线程都需要同时添加同步代码块

    package com.by.test2;
    
    import com.by.util.MyList;
    
    public class TestMyList {
        public static void main(String[] args)throws Exception {
            //创建两个线程,同时操作工具类,线程1负责往集合中添加元素1-5,线程2负责往集合中添加元素6-10
            //添加结束之后查看集合内容
            //创建工具类对象
            MyList m = new MyList();
            Thread t1=new Thread(()->{
                for (int i = 1; i <=5 ; i++) {
                    synchronized (m) {
                        m.insert(i);
                    }
                }
            });
    
            Thread t2=new Thread(()->{
                for (int i = 6; i <=10 ; i++) {
                    synchronized (m) {
                        m.insert(i);
                    }
                }
            });
    
            t1.start();
            t2.start();
            //使t1和t2线程先进行添加操作
            t1.join();
            t2.join();
    
            //查看集合元素
            m.query();
        }
    }
    
    
    /*
    * 张三上厕所
    * 李四上厕所
    *
    * 原子操作: 脱裤子-->蹲下来-->上厕所-->擦屁股-->穿裤子-->冲水-->走人
    *
    *临界资源: 厕所-坑位
    *
    *解决方式1:给厕所大门加锁
    *解决方式2:自己给坑位加锁
    * 
    *
    * */
    
区别
  1. 同步方法: 线程执行需要同时争抢时间片和锁标记,写法简单但效率较慢
  2. 同步代码块: 线程只需要争抢时间片, 开启互斥锁的线程默认拥有锁标记, 效率较快但写法相对繁琐

线程安全的集合类

悲观锁: 悲观的认为集合一定会出现线程安全问题,所以直接加锁

乐观锁: 乐观的认为集合一定不会出现线程安全问题,如果安全问题发生,再利用算法解决问题(无锁机制)

JDK5.0,发布了一批无锁机制的线程安全的集合类

都来自于java.util.concurrent包

  1. ConcurrentHashMap: CAS算法

    compare and swap: 比较并交换

    原有值,预期值,结果值: 当原有值与预期值相等时才会将结果值放入内存

    int i=1;

    i++;

    原有值: 1 预期值: 1 结果值:2

  2. CopyOnWriteArrayList:

    • 当集合进行写(增删改)操作时,会先复制出一个副本,在副本中进行写操作,如果过程中出现线程安全问题,则舍弃当前副本,重新复制新的副本重复操作,直至副本中无异常,再将集合引用地址转换向副本地址,一次确保原集合中一定不会发生安全问题
    • 特点: 舍弃写的效率提高读的效率,适用于读操作远多于写操作时
  3. CopyOnWriteArraySet:

    • 原理与CopyOnWriteArrayList一致, 在写时会对元素进行去重

今日掌握

  1. 进程和线程的特点
  2. 线程的两种创建方式
  3. 线程的基础状态及触发时机
  4. sleep和join的区别
  5. 线程池的作用的使用
  6. Runnable和Callable的区别
  7. 什么是线程安全问题?
  8. 如何解决线程安全问题
  9. 同步方法和同步代码块的区别
  10. 线程安全的集合类及原理

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

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

相关文章

INS的各类方程---微分方程

INS 会受到误差的扰动&#xff0c; INS 的误差方程进行推导。常用的误差模型有 PHI 角和 PSI 角误差 模型。PHI 角误差模型是基于平台坐标系与真实导航坐标系推导的&#xff0c;误差模型易于理解但形式较为复杂。PSI 角误差模型是基于平台坐标系和计算坐标系推导的&#xff0c;…

【C++】可变参数模板使用总结(简洁易懂,详细,含代码演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

TYPE C 接口知识

1、Type C 概述 Type-C口有4对TX/RX分线&#xff0c;2对USBD/D-&#xff0c;一对SBU&#xff0c;2个CC&#xff0c;另外还有4个VBUS和4个地线。 当Type-C接口仅用作传输DP信号时&#xff0c;则可利用4对TX/RX&#xff0c;从而实现4Lane传输&#xff0c;这种模式称为DPonly模式…

个性化邮件营销策略:提升销售额的有效方法

事实上&#xff0c;电子邮件营销人员一直将个性化视为让受众产生强烈参与感的最佳方式之一。对于很多营销人员来说&#xff0c;实施个性化甚至不再是一种选择&#xff0c;而是培养和吸引潜在客户和联系人的必要条件。因此&#xff0c;今天我们将一起来讨论一些成功电子邮件营销…

【LeetCode刷题笔记】动态规划(一)

376. 摆动序列 解题思路: 1. 动态规划 , 定义 up[i] 表示下标 i 的元素为 结尾 的【 最长上升摆动序列 】的 长度 , down[i] 表示下标 i 的元素为

2016年第五届数学建模国际赛小美赛B题直达地铁线路解题全过程文档及程序

2016年第五届数学建模国际赛小美赛 B题 直达地铁线路 原题再现&#xff1a; 在目前的大都市地铁网络中&#xff0c;在两个相距遥远的车站之间运送乘客通常需要很长时间。我们可以建议在两个长途车站之间设置直达班车&#xff0c;以节省长途乘客的时间。   第一部分&#xf…

【WPF.NET开发】创建模板

本文内容 何时创建 ControlTemplate先决条件创建 ControlTemplate使用模板添加触发器使用 VisualState 使用 Windows Presentation Foundation (WPF)&#xff0c;可以使用自己的可重用模板自定义现有控件的可视结构和行为。 可以对应用程序、窗口和页面全局应用模板&#xff…

【Amazon 实验②】使用缓存策略及源请求策略,用于控制边缘缓存的行为及回源行为

文章目录 1. 了解缓存策略和源请求策略1.1 使用缓存键和缓存策略 实验&#xff1a;使用CloudFront缓存策略和缓存键控制缓存行为 接上一篇文章【Amazon 实验①】使用 Amazon CloudFront加速Web内容分发&#xff0c;我们现在了解和配置如何使用缓存策略及源请求策略&#xff0c;…

JUC AQS ReentrantLock源码分析

AQS java.util.concurrent.locks.AbstractQueuedSynchronizer AQS &#xff08;抽象队列同步器&#xff09;&#xff1a; AbstractQueuedSynchronizer 是什么 来自jdk1.5&#xff0c;是用来实现锁或者其他同步器组件的公共基础部分的抽象实现&#xff0c;是重量级基础框架以及…

java SSM家庭财务管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM家庭财务管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

Win10 使用 Nmap 扫描 Andorid 设备开放端口

Nmap Nmap 是 网络探测工具和安全/端口扫描器。 官网链接 Nmap参考指南(Man Page) 官网下载地址 Downloading Nmap Nmap 下载安装 到官网下载对应操作系统的安装包&#xff0c; 默认配置&#xff0c;一直下一步安装即可。安装过程中备份下安装路径&#xff0c;后续用到。…

❀My学习Linux小记录之UID和GID(用户ID和组ID)❀

❀My学习Linux小记录之UID和GID&#xff08;用户ID和组ID&#xff09;❀ 目录 ❀My学习Linux小记录之UID和GID&#xff08;用户ID和组ID&#xff09;❀ 用户ID&#xff08;UID&#xff09; 组ID&#xff08;GID&#xff09; 登陆 Linux 系统时&#xff0c;虽然输入的是自己…

3D 纹理贴图基础知识

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 介绍 纹理贴图是创建模型时离不开的最后一块拼图。同样&#xff0c;…

Go自定义PriorityQueue优先队列使用Heap堆

题目 分析 每次找最大的&#xff0c;pop出来 然后折半&#xff0c;再丢进去 go写法 go如果想用heap&#xff0c;要实现less\len\swap\push\pop 但可以偷懒&#xff0c;用sort.IntSlice,已经实现了less\len\swap 但由于目前是大根堆&#xff0c;要重写一下less 因此&#xff…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)ChannelMap 模块的实现

&#xff08;三&#xff09;ChannelMap 模块的实现 这个模块其实就是为Channel来服务的&#xff0c;前面讲了Channel这个结构体里边它封装了文件描述符。假如说我们得到了某一个文件描述符&#xff0c;需要基于这个文件描述符进行它对应的事件处理&#xff0c;那怎么办呢&…

【Web】面向小白的CTF中搭docker常用命令

目录 准备 搭建容器 有docker-compose 无docker-compose 只给tar包 查看容器各项信息 销毁容器 最近总有师傅问docker怎么搭&#xff0c;一个一个回比较麻烦&#xff0c;干脆写一篇文章。 准备 你需要准备一个安装了docker的vps&#xff0c;还要一个终端管理工具&…

测试开发体系介绍——测试体系介绍-L2

目录&#xff1a; 被测系统架构与数据流分析 开源项目 LiteMall 系统架构&#xff1a;开源项目 Mall 的系统架构&#xff1a;如何快速了解一家公司的架构统一建模语言 UML推荐工具梳理业务流程&#xff1a;使用思维导图分析功能点:使用时序图分析数据流:使用活动图分析测试用例…

如何使用 NFTScan NFT API 在 Base 网络上开发 Web3 应用

Base 是 Coinbase 使用 OP Stack 开发的最新以太坊第 2 层&#xff08;L2&#xff09;网络&#xff0c;用于解决以太坊等主要区块链面临的可扩展性和成本挑战。Coinbase 将其描述为“安全、低成本、对开发人员友好的以太坊 L2&#xff0c;旨在将下一个 10 亿用户带入 Web3”。B…

MFC 自定义压缩,解压缩工具

界面效果如下&#xff1a; 对外提供的接口如下&#xff1a; public: void setCallback(zp::Callback callback, void* param); bool open(const zp::String& path, bool readonly false); bool create(const zp::String& path, const zp::String& inputPath)…

医保购药小程序:智能合约引领医疗数字革新

在医疗领域&#xff0c;医保购药小程序通过引入智能合约技术&#xff0c;为用户提供更为高效、安全的购药体验。本文将通过简单的智能合约代码示例&#xff0c;深入探讨医保购药小程序如何利用区块链技术中的智能合约&#xff0c;实现医保结算、购药监控等功能&#xff0c;为医…