我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JUC并发编程中进程与线程的示例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JUC并发编程中进程与线程的示例分析

这篇文章将为大家详细讲解有关JUC并发编程中进程与线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    进程与线程

    进程

    • 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的

    • 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。

    • 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)

    线程

    线程是主要负责运行指令,进程是主要管加载指令。

    一个进程之内可以分为一到多个线程。

    一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行

    Java 中,线程作为最小调度单位,进程作为资源分配的最小单位。 在 windows 中进程是不活动的,只是作为线程的容器

    同步异步

    • 需要等待结果返回,才能继续运行就是同步

    • 不需要等待结果返回,就能继续运行就是异步

    串行并行执行时间

    使用多核cpu并行执行可以明显的提高执行效率

    • 串行执行时间 = 各个线程时间累加和 + 汇总时间

    • 并行执行时间 = 最慢的线程时间 + 汇总时间

    注意:单核依然是并发的思想(即:cpu轮流去执行线程,微观上仍旧是串行),使用单核的多线程可能会比使用单核的单线程慢,这是因为多线程上下文切换反而浪费了时间。

    创建和运行线程

    使用 Thread

    public static void main(String[] args) {        // 创建线程对象        Thread t = new Thread("线程1") {            public void run() {                // 要执行的任务                log.debug("线程1被启动了");            }        };        // 启动线程        t.start();        log.debug("测试");    }

    使用 Runnable 配合 Thread

    public static void main(String[] args) {        Runnable runnable = new Runnable() {            public void run() {                // 要执行的任务                log.debug("线程1被启动了");            }        };        // 创建线程对象        Thread t = new Thread(runnable);        t.setName("线程1");        // 启动线程        t.start();        log.debug("测试");    }

    这里的Runnable是一个接口,接口中只有一个抽象方法,由我们来提供实现,实现中包含线程的代码就可以了。

    @FunctionalInterfacepublic interface Runnable {        public abstract void run();}

    Thread 与 Runnable 的关系原理分析

    方法1原理分析

    方法2是使用runnable对象,当成参数传给Thread构造方法,其中又调用了init方法,下面是Thread构造方法的源码

    public Thread(Runnable target) {        init(null, target, "Thread-" + nextThreadNum(), 0);    }

    继续跟踪查看runnable对象传到哪里去了,可以看到又传给了另一个重载的init,如下

    private void init(ThreadGroup g, Runnable target, String name,                      long stackSize) {        init(g, target, name, stackSize, null, true);    }private void init(ThreadGroup g, Runnable target, String name,                      long stackSize) {        init(g, target, name, stackSize, null, true);    }

    再次跟踪可以看到是把runnable对象传给了一个thread的一个成员变量

    //省略部分代码this.target = target;

    那么这个成员变量在哪里在使用了呢,经过查找可以发现是在run方法里面,只不过Thread发现有runnable对象就会先采用runnable的run方法。

    @Override    public void run() {        if (target != null) {            target.run();        }    }

    方法2原理分析

    通过创建一个子类去重写Thread类的run方法,这样就不会执行父类的run方法。

    用 Runnable 更容易与线程池等高级 API 配合

    用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

    方法3 FutureTask配合Thread创建线程

    JUC并发编程中进程与线程的示例分析

    Future接口中含有get方法来返回结果的

    //省略部分代码V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;

    而runnable本身是没有返回结果的,runnable不能将结果传给其他线程。

    public interface Runnable {    public abstract void run();}

    要注意到FutureTask也实现了Runnable接口,也可以传给Thread的有参构造里面。

    创建线程的代码

    public static void main(String[] args) throws ExecutionException, InterruptedException {        // 创建任务对象        FutureTask<Integer> task3 = new FutureTask<>(new Callable<Integer>() {            @Override            public Integer call() throws Exception {                log.debug("线程1被执行了");                return 666;            }        });        // 参数1 是任务对象; 参数2 是线程名字,推荐        new Thread(task3, "线程1").start();        // 主线程阻塞,同步等待 task 执行完毕的结果        Integer result = task3.get();        log.debug("结果是:{}", result);    }

    查看进程

    • 任务管理器可以查看进程和线程数,也可以用来杀死进程,也可以在控制台使用tasklist查看进程taskkill杀死进程

    • jconsole 远程监控配置来查看

    线程运行原理

    JVM 中由堆、栈、方法区所组成,其中栈就是给线程使用的。

    JUC并发编程中进程与线程的示例分析

    方法调用时,就会对该方法产生一个栈帧,方法的局部变量都会在栈帧中存储。栈是后进先出,当method2执行完就会回收,在执行完同时会记录返回地址,然后在method1中继续执行。

    线程之间的栈帧是相互独立的,之间互不干扰。

    线程上下文切换

    当上下文切换时,要保存当前的状态,因为可能是时间片用完了,此时线程还没有结束。Java中对应的就是程序计数器

    start与run方法

    启动一个线程必须要用start方法,如果直接调用类里面的run方法实际走的是main主线程。

    线程start前getState()得到的是NEW

    线程start后getState()得到的是RUNNABLE

    public static void main(String[] args) {        Thread t1 = new Thread("t1") {            @Override            public void run() {                log.debug("t1被启动");            }        };        System.out.println(t1.getState());        t1.start();        System.out.println(t1.getState());    }

    sleep方法

    在sleep期间调用getState()方法可以得到TIMED_WAITING

    public static void main(String[] args) {        Thread t1 = new Thread("线程1") {            @Override            public void run() {                try {                    Thread.sleep(3000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        };        t1.start();        log.debug("线程1 state: {}", t1.getState());        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }        log.debug("线程1 state: {}", t1.getState());    }

    sleep打断

    sleep可以使用interrupt方法打断,打断后会触发InterruptedException异常

    public static void main(String[] args) throws InterruptedException {        Thread t1 = new Thread("t1") {            @Override            public void run() {                log.debug("进入睡眠");                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    log.debug("被唤醒");                    e.printStackTrace();                }            }        };        t1.start();        Thread.sleep(1000);        log.debug("打断");        t1.interrupt();    }

    sleep防止cpu使用100%

    在没有利用cpu来计算时,不要让while(true)空转浪费cpu,这时可以使用yield或 sleep 来让出cpu的使用权给其他程序

    yield方法会把cpu的使用权让出去,然后调度执行其它线程。线程的调度最终还是依赖的操作系统的调度器。

    join方法

    该方法会等待线程的结束

    static int r = 11;    public static void main(String[] args) throws InterruptedException {        test1();    }    private static void test1() throws InterruptedException {        log.debug("主线程开始");        Thread t1 = new Thread(() -> {            sleep(1);            r = 888;        },"线程1");        t1.start();//        t1.join();        log.debug(String.valueOf(r));        log.debug("主线程线程结束");    }

    join没有使用时,返回的是11,若是使用join返回的是888,是主线程在同步等待线程1。

    当然还有其他的方法等待

    sleep方法等待线程1结束

    利用FutureTask的get方法

    join(long n)方法可以传入参数,等待线程运行结束,最多等待 n 毫秒,假如执行时间大于等待的时间,就会不再等待。那么该线程会直接结束吗?答案是不会。

    如下代码

    public class Test10 {    static int r = 11;    public static void main(String[] args) throws InterruptedException {        test1();    }    private static void test1() throws InterruptedException {        log.debug("主线程开始");        Thread t1 = new Thread(() -> {            sleep(2);            r = 888;            log.debug("线程1结束");        },"线程1");        t1.start();        t1.join(1000);        log.debug(String.valueOf(r));        log.debug("主线程线程结束");    }}

    输出结果,可以看到这里只是主线程不再等待。

    53.360 c.Test10 [main] - 主线程开始
    16:28:54.411 c.Test10 [main] - 11
    16:28:54.411 c.Test10 [main] - 主线程线程结束
    16:28:55.404 c.Test10 [线程1] - 线程1结束

    interrupt 方法

    interrupt可以用来打断处于阻塞状态的线程。在打断后,会有一个打断标记(布尔值)会提示是否被打断过,被打断过标记为true否则为false.
    但是sleep、wait和join可以来清空打断标记。

    代码如下

    public static void main(String[] args) throws InterruptedException {        Thread t1 = new Thread(() -> {            log.debug("sleep...");            try {                Thread.sleep(4000);            } catch (InterruptedException e) {                e.printStackTrace();            }        },"t1");        t1.start();        Thread.sleep(1000);        log.debug("interrupt");        t1.interrupt();        log.debug("打断标记:{}", t1.isInterrupted());    }

    线程被打断后并不会结束运行,有人就会问了,那我们如何在打断线程后关闭线程呢?答案就是利用打断标记去实现。

    可以在线程的死循环之中加入一个判断去实现。

    boolean interrupted = Thread.currentThread().isInterrupted();                if(interrupted) {                    log.debug("退出循环");                    break;                }

    守护进程

    Java 进程通常需要所有线程都运行结束,才会结束。

    但是存在一种守护进程,只要其他非守护进程结束,守护进程就会结束。垃圾回收器就使用的守护进程。

    线程的状态

    操作系统层面(早期进程的状态)

    JUC并发编程中进程与线程的示例分析

    • 初始状态 在语言层面创建了线程对象,还未与操作系统线程关联

    • 可运行状态(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行任务。

    • 运行状态 获取了 CPU 时间片运行中的状态

    • 调用阻塞api使运行状态转为阻塞状态

    • 终止状态 表示线程已经执行完毕

    Java API 层面

    JUC并发编程中进程与线程的示例分析

    1、新建状态(New)

    Thread t1 = new Thread("t1") {            @Override            public void run() {                log.debug("running...");            }        };log.debug("t1 state {}", t1.getState());

    2、就绪状态(Runnable)与运行状态(Running)

    Thread t2 = new Thread("t2") {            @Override            public void run() {                while(true) { // runnable                }            }        };t2.start();log.debug("t2 state {}", t2.getState());

    3、阻塞状态(Blocked)

    用一个线程拿到锁,使得当前线程没拿到锁会出现阻塞状态。

    Thread t6 = new Thread("t6") {            @Override            public void run() {                synchronized (TestState.class) { // blocked                    try {                        Thread.sleep(90000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };t6.start();

    4、等待状态(Waiting)

    等待一个未执行完成的线程

    t2.join(); //等待状态

    5、超时等待(Time_Waiting)

    可以在指定的时间自行返回的。

    6、终止状态(TERMINATED)

    线程执行完了或者因异常退出了run()方法,该线程结束生命周期。 终止的线程不可再次复生。

    关于“JUC并发编程中进程与线程的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    JUC并发编程中进程与线程的示例分析

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    JUC并发编程中进程与线程的示例分析

    这篇文章将为大家详细讲解有关JUC并发编程中进程与线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。进程与线程进程程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,
    2023-06-29

    Java多线程并发、并行、线程与进程实例分析

    本篇内容介绍了“Java多线程并发、并行、线程与进程实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、并发与并行并发:指两个或多个事
    2023-07-02

    Java并发编程之线程池的示例分析

    这篇文章将为大家详细讲解有关Java并发编程之线程池的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是线程池是一种基于池化思想管理线程的工具。池化技术:池化技术简单点来说,就是提前保存大量的资
    2023-06-20

    Java中多线程与并发的示例分析

    这篇文章主要介绍Java中多线程与并发的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、进程与线程进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。线程:是进程的一个执行路径,一个
    2023-06-15

    Javaweb进程与线程的示例分析

    这篇文章将为大家详细讲解有关Javaweb进程与线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、认识进程1,线程初识一个可执行程序运行起来,就可以看作是一个进程进程也是操作系统分配资源的最
    2023-06-29

    七、并发编程(进程与线程)

    一、前言进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。操作系统的其他所有内容都是围绕进程的概念展开的。   PS:即使可以利用的
    2023-01-30

    Go并发编程的示例分析

    这篇文章给大家分享的是有关Go并发编程的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、goroutine定义给函数前加上go即可不需要在定义是区分是否是异步函数调度器在合适的点进行切换,这个点是有很多
    2023-06-20

    JUC并发编程LinkedBlockingQueue队列源码分析

    这篇文章主要介绍了JUC并发编程LinkedBlockingQueue队列源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JUC并发编程LinkedBlockingQueue队列源码分析文章都会有所收获,
    2023-07-06

    Java并发中守护线程的示例分析

    今天就跟大家聊聊有关Java并发中守护线程的示例分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemo
    2023-06-17

    java并发编程工具类JUC之LinkedBlockingQueue链表队列的示例分析

    小编给大家分享一下java并发编程工具类JUC之LinkedBlockingQueue链表队列的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!java.u
    2023-06-15

    Linux中进程和线程的示例分析

    这篇文章主要为大家展示了“Linux中进程和线程的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Linux中进程和线程的示例分析”这篇文章吧。计算机实际上可以做的事情实质上非常简单,比如
    2023-06-13

    Node.js中进程和线程的示例分析

    这篇文章给大家分享的是有关Node.js中进程和线程的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。进程与线程是一个程序员的必知概念,面试经常被问及,但是一些文章内容只是讲讲理论知识,可能一些小伙伴并没有
    2023-06-15

    java线程编程的示例分析

    这篇文章给大家分享的是有关java线程编程的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。线程基础◆线程(thread)其实是控制线程(thread of control)的缩写.每一个线程都是独立的,因
    2023-06-03

    java中进程和线程的示例分析

    小编给大家分享一下java中进程和线程的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么会有进程在简单的批处理操作系统中,作业时串行执行的,即一个作业
    2023-06-20

    Java并发编程之线程状态实例分析

    今天小编给大家分享一下Java并发编程之线程状态实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。线程状态概述线程由生到
    2023-06-30

    java多线程编程的示例分析

    这篇文章将为大家详细讲解有关java多线程编程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一.相关知识:Java多线程程序设计到的知识:(一)对同一个数量进行操作(二)对同一个对象进行操作(三
    2023-05-30

    JUC并发编程LinkedBlockingQueue队列深入分析源码

    LinkedBlockingQueue是一个可选有界阻塞队列,这篇文章主要为大家详细介绍了Java中LinkedBlockingQueue的实现原理与适用场景,感兴趣的可以了解一下
    2023-05-15

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录