精华内容
下载资源
问答
  • Java 进程 线程

    2019-06-28 16:22:08
    进程线程的区别 进程让操作系统并发成为可能 线程进程内子任务并发成为可能 ...java对操作系统提供的功能进行封装,包括进程线程 运行一个程序会产生一个进程进程包含至少一个线程 每个进程对应一个jvm实...

    进程和线程的区别
    在这里插入图片描述
    进程让操作系统并发成为可能
    线程让进程内子任务并发成为可能

    在这里插入图片描述
    在这里插入图片描述

    区别 线程 进程
    独立应用
    独立空间 否,只是进程执行的不同路径 独立地址空间,相互之间不影响
    健壮性
    切换开销

    进程、线程关系

    • java对操作系统提供的功能进行封装,包括进程和线程
    • 运行一个程序会产生一个进程,进程包含至少一个线程
    • 每个进程对应一个jvm实例,多个线程共享jvm里的堆
    • java采用单线程编程模式,程序会自动创建主线程
    • 主线程可以创建子线程,原则上要后于子线程完成执行

    Thread start run

    start run
    线程 子线程 主线程

    在这里插入图片描述
    Thread和Runnable关系

    在这里插入图片描述
    如何给run()方法传参

    • 构造函数传参
    • 成员变量传参
    • 回调函数传参

    传参示例

    如何实现处理线程的返回值

    • 主线程等待法 Thread.sleep
    • 使用thread类的join()阻塞当前的线程以等待子线程处理完毕
    • 通过callable接口实现,通过future task or 线程池获取
    package com.java.thread;
    
    import java.util.concurrent.Callable;
    
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            String value = "test";
            System.out.println("Ready to work!");
            Thread.sleep(5000);
            System.out.println("task done");
            return value;
        }
    }
    
    

    future task

    package com.java.thread;
    
    import java.util.concurrent.FutureTask;
    
    public class FutureTaskDemo {
    
        public static void main(String[] args) {
            FutureTask<String> task = new FutureTask<>(new MyCallable());
            new Thread(task).start();
            if (!task.isDone()) {
                System.out.println("please wait");
            }
            System.out.println("task return " + task.get());
        }
    }
    

    线程池

    package com.java.thread;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    
    public class ThreadPoolDemo {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
            Future<String> future = newCachedThreadPool.submit(new MyCallable());
    
            if (!future.isDone()) {
                System.out.println("please wait");
            }
            
            System.out.println("task return " + future.get());
            newCachedThreadPool.shutdown();
        }
    }
    

    线程的状态

    1. 新建 new:创建后尚未启动
    2. 运行 runnable:包含running和ready
    3. 无限等待 waiting:不会被分配cpu执行时间,需要显示被唤醒 【未设置timeout的 Object.wait() 和 Thread.join() 以及LockSupport.park()】
    4. 限期等待 timed waiting:在一定的时间后会由系统自动唤醒【设置timeout的 Object.wait() 和 Thread.join() 以及LockSupport.parkNanos(),LockSupport.parkUntil(), Thread.sleep()】
    5. 阻塞 blocked:等待获取排它锁
    6. 结束 terminated:终止线程状态,已经结束执行

    sleep和wait的区别

    sleep wait
    方法 Thread类 Object类中定义
    使用 任何地方 synchronized方法或synchronized块中
    功能 只会让出cpu,不会导致锁行为的改变 不仅让出cpu,还会释放已经占有的同步锁的资源

    notify和notifyall

    • 锁池 EntryList
    • 等待池 WaitSet

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    yield
    在这里插入图片描述
    对锁的行为不会有影响

    如何中断线程
    已经被抛弃的方法

    • stop
    • suspend resume

    目前使用的
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 一、进程和线程的区别1,进程和线程都有三种基本状态:就绪,运行,阻塞2,从资源的角度讲:进程是拥有资源的基本单位,线程...线程由线程ID、当前指令指针、寄存器集合和堆栈等组成,线程切换只需要保存和设置少量...

    一、进程和线程的区别

    1,进程和线程都有三种基本状态:就绪,运行,阻塞

    2,从资源的角度讲:进程是拥有资源的基本单位,线程可以共享其隶属进程的系统资源。

    3,从调度的角度讲:线程是cpu调度的基本单位。

    4,从系统开销的角度讲:进程由程序、数据、进程控制块三部分组成。每次创建进程,系统都要为之分配或回收资源,如内存、io等。

    线程由线程ID、当前指令指针、寄存器集合和堆栈等组成,线程切换只需要保存和设置少量寄存器变量,因此系统开销小。

    5,从通信方面:进程通信需要依靠操作系统,而线程可以直接读写进程数据段(全局变量)来进行通信。

    进程通信可以采用的方法有:管道、信号、信号量、消息队列、共享内存、状态变量、套接字(socket)

    线程通信貌似也有类似的一些东西

    二、java中多线程的实现方式

    1,线程创建

    1.1继承Thread

    1)定义Thread的子类,实现run()方法

    2)创建Thread子类的对象  [创建之后,线程处于新建状态]

    3)调用线程对象的start方法  [线程处于就绪状态]

    1.2实现runnable或者callable

    1)定义类,实现runnable接口,重写run方法

    2)创建上述类类的对象  [创建之后,线程处于新建状态]

    3)不直接调用上述对象的start方法,而是将其作为target。new Thread(对象).start()  [线程处于就绪状态]

    2,控制线程的相关方法

    1)join 让一个线程等待另一个线程完成。调用后,当前线程阻塞,直到被调用的线程执行结束。

    2)sleep让线程休眠,由运行态进入阻塞态。

    3)yield让线程重新调度,有运行态进入就绪态。

    3,线程同步

    1)同步代码块,利用Synhronized(obj){}修饰一段代码

    2)同步方法,将方法使用Synhronized修饰

    3)同步锁Lock,和同步方法比较类似,只是同步锁显示使用Lock对象作为同步锁

    4,线程通信

    1)全局变量

    2)使用Object类的三个方法:该方法有同步监视器对象调用

    wait:导致当前进程等待

    notify:唤醒在该同步监视器上等待的进程(随机选一)

    notifyAll:唤醒在该同步监视器上等待的所有进程

    展开全文
  • Java进程线程(一)

    2020-06-26 22:53:28
    进程线程概念 进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。 线程:线程是进程中的一个执行单元,负责当前进程中程序的...

    1.进程线程概念
    进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

    2.举例说明
    360软件进入内存运行
    效率取决于cpu, 早期的cpu只有一核,cpu会在多个线程之间进行高速的切换,现在cpu是多核,每一个程序的执行路径就是一个线程

    在这里插入图片描述迅雷下载比浏览器下载速度快得多,比如下载qq.exe, 迅雷开了多个线程,同时分批读取字节,而浏览器一般是单线程,一次只读取部分字节

    3.程序如何运行
    分时调度:
    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间

    抢占式调度:
    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

    实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
    其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

    4.主线程main

    在这里插入图片描述

    展开全文
  • 刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。Java线程的实现在「JDK1.2之后」,...
    cc7589115aa7a2d28c7b3d9a51bb9a85.png

    前提

    最近有点懒散,没什么比较有深度的产出。刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。

    Java线程的实现

    「JDK1.2之后」,Java线程模型已经确定了基于操作系统原生线程模型实现。因此,目前或者今后的JDK版本中,操作系统支持怎么样的线程模型,在很大程度上决定了Java虚拟机的线程如何映射,这一点在不同的平台上没有办法达成一致,虚拟机规范中也未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对于Java程序来说,这些差异是透明的。

    对应Oracle Sun JDK或者说Oracle Sun JVM而言,它的Windows版本和Linux版本都是使用「一对一的线程模型」实现的(如下图所示)。

    a8408158c388fc7ad9e6e0647a39902a.png
    j-t-l-s-1.png

    也就是一条Java线程就映射到一条轻量级进程(「Light Weight Process」)中,而一条轻量级线程又映射到一条内核线程(「Kernel-Level Thread」)。我们平时所说的线程,往往就是指轻量级进程(或者通俗来说我们平时新建的java.lang.Thread就是轻量级进程实例的一个"句柄",因为一个java.lang.Thread实例会对应JVM里面的一个JavaThread实例,而JVM里面的JavaThread就应该理解为轻量级进程)。前面推算这个线程映射关系,可以知道,我们在应用程序中创建或者操作的java.lang.Thread实例最终会映射到系统的内核线程,如果我们恶意或者实验性无限创建java.lang.Thread实例,最终会影响系统的正常运行甚至导致系统崩溃(可以在Windows开发环境中做实验,确保内存足够的情况下使用死循环创建和运行java.lang.Thread实例)。

    线程调度方式包括两种,协同式线程调度和抢占式线程调度。

    线程调度方式描述劣势优势
    协同式线程调度线程的执行时间由线程本身控制,执行完毕后主动通知操作系统切换到另一个线程上某个线程如果不让出CPU执行时间可能会导致整个系统崩溃实现简单,没有线程同步的问题
    抢占式线程调度每个线程由操作系统来分配执行时间,线程的切换不由线程自身决定实现相对复杂,操作系统需要控制线程同步和切换不会出现一个线程阻塞导致系统崩溃的问题

    Java线程最终会映射为系统内核原生线程,所以Java线程调度最终取决于系操作系统,而目前主流的操作系统内核线程调度基本都是使用抢占式线程调度。也就是可以死记硬背一下:「Java线程是使用抢占式线程调度方式进行线程调度的」

    很多操作系统都提供线程优先级的概念,但是由于平台特性的问题,Java中的线程优先级和不同平台中系统线程优先级并不匹配,所以Java线程优先级可以仅仅理解为“「建议优先级」”,通俗来说就是java.lang.Thread#setPriority(int newPriority)并不一定生效,「有可能Java线程的优先级会被系统自行改变」

    Java线程的状态切换

    Java线程的状态可以从java.lang.Thread的内部枚举类java.lang.Thread$State得知:

    public enum State {
          
        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }

    这些状态的描述总结成图如下:

    91c960dabf6661674381b779cc14c6c5.png
    j-t-l-s-3

    「线程状态之间关系切换」图如下:

    c9f5c2d51930e1c72655c0b380f76103.png
    j-t-l-s-2

    下面通过API注释和一些简单的代码例子分析一下Java线程的状态含义和状态切换。

    NEW状态

    「API注释」

    /**
     * Thread state for a thread which has not yet started.
     *
     */

    NEW,

    线程实例尚未启动时候的线程状态。

    一个刚创建而尚未启动(尚未调用Thread#start()方法)的Java线程实例的就是处于NEW状态。

    public class ThreadState {

        public static void main(String[] args) throws Exception {
            Thread thread = new Thread();
            System.out.println(thread.getState());
        }
    }

    // 输出结果
    NEW

    RUNNABLE状态

    「API注释」

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */

    RUNNABLE,

    可运行状态下线程的线程状态。可运行状态下的线程在Java虚拟机中执行,但它可能执行等待操作系统的其他资源,例如处理器。

    当Java线程实例调用了Thread#start()之后,就会进入RUNNABLE状态。RUNNABLE状态可以认为包含两个子状态:READYRUNNING

    • READY:该状态的线程可以被线程调度器进行调度使之更变为RUNNING状态。
    • RUNNING:该状态表示线程正在运行,线程对象的run()方法中的代码所对应的的指令正在被CPU执行。

    当Java线程实例Thread#yield()方法被调用时或者由于线程调度器的调度,线程实例的状态有可能由RUNNING转变为READY,但是从线程状态Thread#getState()获取到的状态依然是RUNNABLE。例如:

    public class ThreadState1 {

        public static void main(String[] args) throws Exception {
            Thread thread = new Thread(()-> {
                while (true){
                    Thread.yield();
                }
            });
            thread.start();
            Thread.sleep(2000);
            System.out.println(thread.getState());
        }
    }
    // 输出结果
    RUNNABLE

    WAITING状态

    「API注释」

       /**
        * Thread state for a waiting thread.
        * A thread is in the waiting state due to calling one of the
        * following methods:
        * 

        *   {@link Object#wait() Object.wait} with no timeout
        *   {@link #join() Thread.join} with no timeout
        *   {@link LockSupport#park() LockSupport.park}
        * 
        *
        * 

    A thread in the waiting state is waiting for another thread to
        * perform a particular action.
        *
        * For example, a thread that has called {@code Object.wait()}
        * on an object is waiting for another thread to call
        * {@code Object.notify()} or {@code Object.notifyAll()} on
        * that object. A thread that has called {@code Thread.join()}
        * is waiting for a specified thread to terminate.
        */


        WAITING,

    等待中线程的状态。一个线程进入等待状态是由于调用了下面方法之一:不带超时的Object#wait() 不带超时的Thread#join() LockSupport.park() 一个处于等待状态的线程总是在等待另一个线程进行一些特殊的处理。例如:一个线程调用了Object#wait(),那么它在等待另一个线程调用对象上的Object#notify()或者Object#notifyAll();一个线程调用了Thread#join(),那么它在等待另一个线程终结。

    WAITING「无限期的等待状态」,这种状态下的线程不会被分配CPU执行时间。当一个线程执行了某些方法之后就会进入无限期等待状态,直到被显式唤醒,被唤醒后,线程状态由WAITING更变为RUNNABLE然后继续执行。

    RUNNABLE转换为WAITING的方法(无限期等待)WAITING转换为RUNNABLE的方法(唤醒)
    Object#wait()Object#notify() | Object#notifyAll()
    Thread#join()-
    LockSupport.part()LockSupport.unpart(thread)

    其中Thread#join()方法相对比较特殊,它会阻塞线程实例直到线程实例执行完毕,可以观察它的源码如下:

    public final void join() throws InterruptedException {
        join(0);
    }

    public final synchronized void join(long millis)throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    可见Thread#join()是在线程实例存活的时候总是调用Object#wait()方法,也就是必须在线程执行完毕isAlive()为false(意味着线程生命周期已经终结)的时候才会解除阻塞。

    基于WAITING状态举个例子:

    public class ThreadState3 {

        public static void main(String[] args) throws Exception {
            Thread thread = new Thread(()-> {
                LockSupport.park();
                while (true){
                    Thread.yield();
                }
            });
            thread.start();
            Thread.sleep(50);
            System.out.println(thread.getState());
            LockSupport.unpark(thread);
            Thread.sleep(50);
            System.out.println(thread.getState());
        }
    }
    // 输出结果
    WAITING
    RUNNABLE

    TIMED WAITING状态

    「API注释」

    /**
    * Thread state for a waiting thread with a specified waiting time.
    * A thread is in the timed waiting state due to calling one of
    * the following methods with a specified positive waiting time:

    *   {@link #sleep Thread.sleep}
    *   {@link Object#wait(long) Object.wait} with timeout
    *   {@link #join(long) Thread.join} with timeout
    *   {@link LockSupport#parkNanos LockSupport.parkNanos}
    *   {@link LockSupport#parkUntil LockSupport.parkUntil}

    */
    TIMED_WAITING,

    定义了具体等待时间的等待中线程的状态。一个线程进入该状态是由于指定了具体的超时期限调用了下面方法之一:Thread.sleep() 带超时的Object#wait() 带超时的Thread#join() LockSupport.parkNanos() LockSupport.parkUntil()

    TIMED WAITING就是「有限期等待状态」,它和WAITING有点相似,这种状态下的线程不会被分配CPU执行时间,不过这种状态下的线程不需要被显式唤醒,只需要等待超时限期到达就会被VM唤醒,有点类似于现实生活中的闹钟。

    RUNNABLE转换为TIMED WAITING的方法(有限期等待)TIMED WAITING转换为RUNNABLE的方法(超时解除等待)
    Object#wait(timeout)-
    Thread#sleep(timeout)-
    Thread#join(timeout)-
    LockSupport.parkNanos(timeout)-
    LockSupport.parkUntil(timeout)-

    举个例子:

    public class ThreadState4 {

        public static void main(String[] args) throws Exception {
            Thread thread = new Thread(()-> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //ignore
                }
            });
            thread.start();
            Thread.sleep(50);
            System.out.println(thread.getState());
            Thread.sleep(1000);
            System.out.println(thread.getState());
        }
    }
    // 输出结果
    TIMED_WAITING
    TERMINATED

    BLOCKED状态

    「API注释」

    /**
    * Thread state for a thread blocked waiting for a monitor lock.
    * A thread in the blocked state is waiting for a monitor lock
    * to enter a synchronized block/method or
    * reenter a synchronized block/method after calling
    * {@link Object#wait() Object.wait}.
    */

    BLOCKED,

    此状态表示一个线程正在阻塞等待获取一个监视器锁。如果线程处于阻塞状态,说明线程等待进入同步代码块或者同步方法的监视器锁或者在调用了Object#wait()之后重入同步代码块或者同步方法。

    BLOCKED状态也就是阻塞状态,该状态下的线程不会被分配CPU执行时间。线程的状态为BLOCKED的时候有两种可能的情况:

    A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method

    1. 线程正在等待一个监视器锁,只有获取监视器锁之后才能进入synchronized代码块或者synchronized方法,在此等待获取锁的过程线程都处于阻塞状态。

    reenter a synchronized block/method after calling Object#wait()

    1. 线程X步入synchronized代码块或者synchronized方法后(此时已经释放监视器锁)调用Object#wait()方法之后进行阻塞,当接收其他线程T调用该锁对象Object#notify()/notifyAll(),但是线程T尚未退出它所在的synchronized代码块或者synchronized方法,那么线程X依然处于阻塞状态(注意API注释中的「reenter」,理解它场景2就豁然开朗)。

    更加详细的描述可以参考笔者之前写过的一篇文章:深入理解Object提供的阻塞和唤醒API

    针对上面的场景1举个简单的例子:

    public class ThreadState6 {

        private static final Object MONITOR = new Object();

        public static void main(String[] args) throws Exception {
            Thread thread1 = new Thread(()-> {
                synchronized (MONITOR){
                    try {
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        //ignore
                    }
                }
            });
            Thread thread2 = new Thread(()-> {
                synchronized (MONITOR){
                    System.out.println("thread2 got monitor lock...");
                }
            });
            thread1.start();
            Thread.sleep(50);
            thread2.start();
            Thread.sleep(50);
            System.out.println(thread2.getState());
        }
    }
    // 输出结果
    BLOCKED

    针对上面的场景2举个简单的例子:

    public class ThreadState7 {

        private static final Object MONITOR = new Object();
        private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        public static void main(String[] args) throws Exception {
            System.out.println(String.format("[%s]-begin...", F.format(LocalDateTime.now())));
            Thread thread1 = new Thread(() -> {
                synchronized (MONITOR) {
                    System.out.println(String.format("[%s]-thread1 got monitor lock...", F.format(LocalDateTime.now())));
                    try {
                        Thread.sleep(1000);
                        MONITOR.wait();
                    } catch (InterruptedException e) {
                        //ignore
                    }
                    System.out.println(String.format("[%s]-thread1 exit waiting...", F.format(LocalDateTime.now())));
                }
            });
            Thread thread2 = new Thread(() -> {
                synchronized (MONITOR) {
                    System.out.println(String.format("[%s]-thread2 got monitor lock...", F.format(LocalDateTime.now())));
                    try {
                        MONITOR.notify();
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        //ignore
                    }
                    System.out.println(String.format("[%s]-thread2 releases monitor lock...", F.format(LocalDateTime.now())));
                }
            });
            thread1.start();
            thread2.start();
            // 这里故意让主线程sleep 1500毫秒从而让thread2调用了Object#notify()并且尚未退出同步代码块,确保thread1调用了Object#wait()
            Thread.sleep(1500);  
            System.out.println(thread1.getState());
            System.out.println(String.format("[%s]-end...", F.format(LocalDateTime.now())));
        }
    }
    // 某个时刻的输出如下:
    [2019-06-20 00:30:22]-begin...
    [2019-06-20 00:30:22]-thread1 got monitor lock...
    [2019-06-20 00:30:23]-thread2 got monitor lock...
    BLOCKED
    [2019-06-20 00:30:23]-end...
    [2019-06-20 00:30:25]-thread2 releases monitor lock...
    [2019-06-20 00:30:25]-thread1 exit waiting...

    场景2中:

    • 线程2调用Object#notify()后睡眠2000毫秒再退出同步代码块,释放监视器锁。
    • 线程1只睡眠了1000毫秒就调用了Object#wait(),此时它已经释放了监视器锁,所以线程2成功进入同步块,线程1处于API注释中所述的reenter a synchronized block/method的状态。
    • 主线程睡眠1500毫秒刚好可以命中线程1处于reenter状态并且打印其线程状态,刚好就是BLOCKED状态。

    这三点看起来有点绕,多看几次多思考一下应该就能理解。

    TERMINATED状态

    「API注释」

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
     
    TERMINATED;

    终结的线程对应的线程状态,此时线程已经执行完毕。

    TERMINATED状态表示线程已经终结。一个线程实例只能被启动一次,准确来说,只会调用一次Thread#run()方法,Thread#run()方法执行结束之后,线程状态就会更变为TERMINATED,意味着线程的生命周期已经结束。

    举个简单的例子:

    public class ThreadState8 {

        public static void main(String[] args) throws Exception {
            Thread thread = new Thread(() -> {

            });
            thread.start();
            Thread.sleep(50);
            System.out.println(thread.getState());
        }
    }
    // 输出结果
    TERMINATED

    上下文切换

    多线程环境中,当一个线程的状态由RUNNABLE转换为非RUNNABLE(BLOCKEDWAITING或者TIMED_WAITING)时,相应线程的上下文信息(也就是常说的Context,包括CPU的寄存器和程序计数器在某一时间点的内容等等)需要被保存,以便线程稍后恢复为RUNNABLE状态时能够在之前的执行进度的基础上继续执行。而一个线程的状态由非RUNNABLE状态进入RUNNABLE状态时可能涉及恢复之前保存的线程上下文信息并且在此基础上继续执行。这里的对「线程的上下文信息进行保存和恢复的过程」就称为上下文切换(Context Switch)。

    线程的上下文切换会带来额外的性能开销,这包括保存和恢复线程上下文信息的开销、对线程进行调度的CPU时间开销以及CPU缓存内容失效的开销(线程所执行的代码从CPU缓存中访问其所需要的变量值要比从主内存(RAM)中访问响应的变量值要快得多,但是「线程上下文切换会导致相关线程所访问的CPU缓存内容失效,一般是CPU的L1 CacheL2 Cache,使得相关线程稍后被重新调度到运行时其不得不再次访问主内存中的变量以重新创建CPU缓存内容)。

    Linux系统中,可以通过vmstat命令来查看全局的上下文切换的次数,例如:

    $ vmstat 1

    对于Java程序的运行,在Linux系统中也可以通过perf命令进行监视,例如:

    $ perf stat -e cpu-clock,task-clock,cs,cache-reference,cache-misses java YourJavaClass

    参考资料中提到Windows系统下可以通过自带的工具perfmon(其实也就是任务管理器)来监视线程的上下文切换,实际上笔者并没有从任务管理器发现有任何办法查看上下文切换,通过搜索之后发现了一个工具:Process Explorer。运行Process Explorer同时运行一个Java程序并且查看其状态:

    01fd7ecebe2229869ae85c8e30975274.png
    j-t-l-s-4.png

    因为打了断点,可以看到运行中的程序的上下文切换一共7000多次,当前一秒的上下文切换增量为26(因为笔者设置了Process Explorer每秒刷新一次数据)。

    监控线程状态

    如果项目在生产环境中运行,不可能频繁调用Thread#getState()方法去监测线程的状态变化。JDK本身提供了一些监控线程状态的工具,还有一些开源的轻量级工具如阿里的Arthas,这里简单介绍一下。

    使用jvisualvm

    jvisualvm是JDK自带的堆、线程等待JVM指标监控工具,适合使用于开发和测试环境。它位于JAVA_HOME/bin目录之下。

    a9bcbee0b1ae45cffd98dca8019848ed.png
    j-t-l-s-5.png

    其中线程Dump的按钮类似于下面要提到的jstack命令,用于导出所有线程的栈信息。

    使用jstack

    jstack是JDK自带的命令行工具,功能是用于获取指定PID的Java进程的线程栈信息。例如本地运行的一个IDEA实例的PID是11376,那么只需要输入:

    jstack 11376

    然后控制台输出如下:

    41af920cd19dd437001a925a24e155ad.png
    j-t-l-s-6.png

    另外,如果想要定位具体Java进程的PID,可以使用jps命令。

    使用JMC

    JMC也就是Java Mission Control,它也是JDK自带的工具,提供的功能要比jvisualvm强大,包括MBean的处理、线程栈已经状态查看、飞行记录器等等。

    58ae7ff58960f7b3cfba3e6e22899bf5.png
    j-t-l-s-7.png

    小结

    理解Java线程状态的切换和一些监控手段,更有利于日常开发多线程程序,对于生产环境出现问题,通过监控线程的栈信息能够快速定位到问题的根本原因(通常来说,目前比较主流的MVC应用(准确来说应该是Servlet容器如Tomcat)都是通过一个线程处理一个单独的请求,当请求出现阻塞的时候,导出对应处理请求的线程基本可以定位到阻塞的精准位置,如果使用消息队列例如RabbitMQ,消费者线程出现阻塞也可以利用相似的思路解决)。

    (本文完 e-a-20200804 c-3-d)

    展开全文
  • 虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。再后来发展到多线程技术,使得在一个程序内部...
  • java进程线程

    2016-12-14 15:25:40
    java进程线程 进程和线程的区别:  进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。  线程:同一类线程共享代码和数据空间,每个线程有独立的...
  • Java线程系列更新中~正式篇:番外篇(神TM番外篇):Java的多线程是一个同时执行多个线程的过程。线程是一个轻量级的子进程,是最小的处理单元。多线程和多进程都用于实现多任务处理。但是,我们使用多线程而不是多...
  • Java进程线程

    2020-06-30 16:11:33
    文章目录1 进程线程出现的原因2 进程线程的区别3 优缺点4 并发与并行 1 进程线程出现的原因 最初,计算机只能接受一些特定的...并且进程保存了程序每个时刻的运行状态,这样就为进程切换提供了可能。 进程的出现
  • Windows 任务管理器,查看或杀死进程 我们可以看到 进程的ID (PID)以及名称 cmd --》 tasklist 查看进程 taskkill 杀死进程...top 按大写 H 切换是否显示线程 top -H -p 查看某个进程(PID)的所有进程 Java jp
  • 进程切换与线程切换的区别?

    千次阅读 多人点赞 2019-07-25 17:00:04
    注意这个题目问的是进程切换与线程切换的区别,不是进程与线程的区别。当然这里的线程指的是同一个进程中的线程。 这个问题能很好的考察面试者对进程和线程的理解深度,有比较高的区分度。 要想正确回答这个问题,...
  • 进程线程的主要区别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个...但是,在进程切换时,耗费资源较大,效率较低。因此,对于一些要求同时进行并且又要共享某些变量的并发操作,只能用多...
  • Java进程线程的区别 进程之间共享信息可通过TCP/IP协议;线程间共享信息通过共用内存 进程是抢占处理及的调度单位;线程属于某个进程,共享其资源。 进程是资源分配的最小单位;线程是CPU调度的最小单位。 线程是...
  • 对于线程切换,第1步是不需要做的,第2是进程线程切换都要做的。所以明显是进程切换代价大 进程调度,切换进程上下文,包括分配的内存,数据段,堆栈段等 线程调度,切换线程上下文,主要切换堆栈,以及各寄存器...
  • 注意这个题目问的是进程切换与线程切换的区别,不是进程与线程的区别。当然这里的线程指的是同一个进程中的线程。这个问题能很好的考察面试者对进程和线程的理解深度,有比较高的区分度。要想正确回答这个问题,面试...
  • java进程线程

    2014-08-09 16:55:00
    一、进程和线程的区别 1,进程和线程都有三种基本状态:就绪,运行,阻塞 2,从资源的角度讲:进程是拥有资源的基本单位,线程可以共享其隶属...线程由线程ID、当前指令指针、寄存器集合和堆栈等组成,线程切换...
  • 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。1.1多线程与多进程进程是指操作系统能同时运行多个任务(程序)。多线程是指在同一程序中有多个顺序流在执行。1.2...
  • 1.线程进程进程是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间线程1、是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程2、线程实际上是在进程...
  • 刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。Java线程的实现在「JDK1.2之后」,...
  • 线程上下文切换对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。由于可能当前...
  • 线程的内存模型32位操作系统的寻址空间为2的32次方,也就是4GB...用户线程需要访问硬件资源的时候需要委托内核线程进行访问,这就涉及到CPU上下文在用户模式和内核模式的切换。因此在使用线程或者进程的时候需要尽量...
  • 在同一进程中,线程切换不会引起进程切换,但不用进程中的线程切换,会引起进程切换。 创建或撤销进程时,系统都要为之分配或回收资源,涉及当前执行进程 的CPU 环境保存 和 新调度进程 的CPU 环境设置,而线程切换...
  • 注意这个题目问的是进程切换与线程切换的区别,不是进程与线程的区别。当然这里的线程指的是同一个进程中的线程。这个问题能很好的考察面试者对进程和线程的理解深度,有比较高的区分度。要想正确回答这个问题,面试...
  • [Java高并发]进程线程

    2020-05-26 11:47:30
    进程线程进程与线程的区别线程上下文切换比进程上下文切换快线程可以拥有独属于自己的资源吗?进程之间常见的通信方式多线程与单线程的关系线程的状态终止线程 4 种方式正常运行结束使用退出标志退出线程Interrupt ...
  •  线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。  线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。  多进程是指操作系统能同时运行多个任务(程序...
  • JAVA-线程进程

    2020-10-06 23:19:31
    进程中的一个执行路径,共享一个内存空间,线程间可以自由切换,并发执行,一个进程最少有一个线程。 2.线程实际上是在进城基础之上的进一步划分,一个进程启动后,里面若干执行路径又可以划分成若干线程。 二...
  • java线程 & 进程

    2020-04-14 01:59:13
    这里来区分一下线程和进程 进程: 每个进程都有独立的代码和数据空间(进程上下文) 进程间的切换会有较大的开销 一个进程包含1--n个线程。 进程是资源分配的最小... 线程切换开销小。 线程是cpu调度的最小...
  • 一、进程线程的由来 串行:初期的计算机智能串行执行任务,...线程:共享进程的内存资源,相互间切换更快速,支持更细粒度的任务控制,使进程内的子任务得以并发执行 二、进程线程的区别 进程是资源分配的最...
  • Java基础 线程进程的区别 进程是计算机运行的一个应用程序,可以并发 而线程进程中运行的。 一个进程中可以同时运行多个线程,这些线程并发运行,cpu不断切换运行的线程来提高运行效率。线程拥有自己的计数器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,605
精华内容 1,042
关键字:

java进程线程切换

java 订阅