并发编程-进程和线程

发布时间:2022-06-27 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了并发编程-进程和线程脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

进程和线程

进程

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

当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)

线程

一个进程之内可以分为一到多个线程。 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行 Java 中,线程作为最小调度单位,进程作为资源分配的最小单位。 在 windows 中进程是不活动的,只是作为线程的容器 同⼀个进程内多个线程之间可以共享代码段、数据段、打开的⽂件等资源,但每个线程各⾃都有⼀套独⽴的寄存器和栈,这样可以确保线程的控制流是相对独⽴的。

对比

  • 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
  • 进程拥有共享的资源,如内存空间等,供其内部的线程共享
  • 进程间通信较为复杂
    • 同一台计算机的进程通信称为 IPC(Inter-process communication)
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
  • 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低

并行与并发

单核 cpu 下,线程实际还是串行执行 的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是 同时运行的 。总结为一句话就是: 微观串行,宏观并行

一般会将这种 线程轮流使用 CPU 的做法称为并发(concurrent)

多核 cpu下,每个 核(core) 都可以调度运行线程,这时候线程可以是并行的。

并发编程-进程和线程

JMH环境搭建

JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件,它会执行程序预热,执行多次测试并平均

简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析。

JMH比较典型的应用场景有:

  • 想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性;
  • 对比接口不同实现在给定条件下的吞吐量,找到最优实现
  • 查看多少百分比的请求在多长时间内完成

生成项目模版

mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=org.openjdk.jmh -DarchetypeArtifactId=jmh-java-benchmark-archetype -DgroupId=com.javaming.study -DartifactId=springboot-concurrent -Dversion=0.0.1-SNAPSHOT

查看进程/线程信息

ps -ef | grep redis  # 查看redis进程
ps -fT -p <PID>  # 查看某个进程(PID)的所有线程

并发编程-进程和线程

top # 按1 显示每个CPU信息 再按切换回统计后CPU信息

并发编程-进程和线程

top -H -p <PID>  # 查看某个进程(PID)的所有线程

并发编程-进程和线程

线程运行原理

并发编程-进程和线程

栈与栈帧

JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码

  • 线程的 cpu 时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch 频繁发生会影响性能

线程常见方法

Sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

join

主线程中加入t1.join()后就必须等待 t1执行完才能继续运行

如果t1.join(1000) 指定等待1秒,则1秒后 j1就会打印会=0 如果t1.join(3000) 指定等待3秒,则2秒后 j1就会打印会=10

static int j1 = 0;
    public static void main(String[] args) throws InterruptedException {

        log.debug("开始运行");
        Thread t1 = new Thread(() -> {
            try {
                sleep(2000);
                j1 = 10;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        //等待线程运行结束
        t1.join(1000);
        log.debug("结束运行 j1=" + j1);
    }

interrupt

private static void testInterrupt() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                // 打断休眠的线程 会清空打断状态
                log.error("被打断 isInterrupt = " + Thread.currentThread().isInterrupted());
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            while (true) {
                // 打断正常运行的线程 不会清空打断状态
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.error("被打断 isInterrupt = " + Thread.currentThread().isInterrupted());
                    break;
                }
            }
        }, "t2");
        t2.start();

        TimeUnit.SECONDS.sleep(1);
        log.debug("interrupt...");
        t1.interrupt();

        t2.interrupt();
    }

interrupted

和 interrupt 方法一样是判断当前线程是否被打断,但是这个方法是静态方法, 调用的时候会清除打断标记

查看打印结果可以知道:

  • 打断休眠的线程 会清空打断状态
  • 打断正常运行的线程 不会清空打断状态

并发编程-进程和线程

两阶段终止模式

在一个线程t1中如何优雅的停止线程t2?

如果使用stop()方法: stop()方法会真正的杀死线程,如果线程锁住了共享资源,那么当他被杀死后就再也没有机会释放锁,其他线程永远无法获取锁

这个时候就依靠两阶段终止模式 来停止线程

并发编程-进程和线程

public class TestStopThread {

    private Thread monitorThread;

    /**
     * 开始监控线程
     */
    public void start(){
        monitorThread = new Thread(() -> {
            while (true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.debug("处理善后工作");
                    break;
                }
                try {
                    TimeUnit.SECONDS.sleep(2);
                    log.debug("执行监控工作");
                } catch (InterruptedException e) {
                }

            }
        }, "monitor");
        monitorThread.start();
    }

    /**
     * 停止监控线程
     */
    public void stop(){
        monitorThread.interrupt();
    }


}

interrupt 对 LockSupport.park()影响

public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("park....");
            LockSupport.park();
            log.debug("unpark...");
            log.debug("打断状态 = " + Thread.currentThread().isInterrupted());

            LockSupport.park();
            log.debug("unpark...");
        }, "t1");
        t1.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
    }

可以看到即使重新LockSupport.park()后 也无法生效 ,因为park只有 打断标记为false时才能继续生效 interrupt 打断park的锁时 打断标记置为true , 即使重新park 也不会生效

并发编程-进程和线程

// log.debug("打断状态 = " + Thread.currentThread().isInterrupted());
log.debug("打断状态 = " + Thread.interrupted());

修改为上面的语句后就可以正常锁住线程

守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

注意

垃圾回收器线程就是一种守护线程 Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

脚本宝典总结

以上是脚本宝典为你收集整理的并发编程-进程和线程全部内容,希望文章能够帮你解决并发编程-进程和线程所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:并发