youyichannel

志于道,据于德,依于仁,游于艺!

0%

JUC知识点_01

【典中典】进程和线程

进程概念

进程是程序的一次执行过程,是系统运行程序的基本单位 => 进程是动态的。

系统运行一个程序就是一个进程从创建、运行到消亡的过程。

线程概念

和进程相似,但线程是一个比进程更小的执行单元。一个进行在其执行的过程中可以产生多个线程。

同类的多个线程共享进程的堆、方法区,每个线程有独有的程序计数器、虚拟机栈和本地方法栈 => 操作系统产生线程、切换线程是比较廉价的 => 线程被称为轻量级线程

Java程序是天生的多线程程序,使用JMX查看Java程序的线程:

public class MultiThread {

private static final Logger logger = LoggerFactory.getLogger(MultiThread.class);

public static void main(String[] args) {
// 获取Java线程管理类
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 仅仅获取线程和线程堆栈信息,并不获取同步的 monitor, synchronizer 信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
logger.info("[{}] {}", threadInfo.getThreadId(), threadInfo.getThreadName());
}
}
}

输出结果:

10:52:30.614 [main] INFO com.juzi.juc.jmx.MultiThread - [5] Monitor Ctrl-Break
10:52:30.616 [main] INFO com.juzi.juc.jmx.MultiThread - [4] Signal Dispatcher
10:52:30.616 [main] INFO com.juzi.juc.jmx.MultiThread - [3] Finalizer
10:52:30.616 [main] INFO com.juzi.juc.jmx.MultiThread - [2] Reference Handler
10:52:30.616 [main] INFO com.juzi.juc.jmx.MultiThread - [1] main

由此可知:一个Java程序的运行是main线程和其他线程同时运行。

进程和线程的关系、区别、优缺点

「JVM运行时的内存结构」

从上图可以看出:一个进程可以存在多个线程,多个线程共享进程的堆、方法区,每个线程独享PC寄存器、虚拟机栈、本地方法栈。

=> 线程是更小的运行单元。进程和线程最大的区别在于各进程基本上是独立的,各线程之间会相互影响;线程执行系统开销小,但不利于资源的管理和保护,进程则刚好相反。

PC寄存器(程序计数器)为什么被设定为私有?

多线程在一个特定的时间段内只会执行其中的某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常性的中断和回复,为了保证分毫不差,同时为了能够准确的记录各个线程正在执行的当前字节码指令地址,最好的办法就是为每一个线程都分配一个PC寄存器,这样依赖各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。

虚拟机栈和本地方法栈为什么是私有的?

1)虚拟机栈:每个Java方法执行前就会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息,从方法调用开始至执行结束的过程,对应着一个栈帧在Java虚拟机栈中入栈和出栈的过程

2)本地方法栈:和虚拟机栈相似,区别是虚拟机栈为虚拟机执行字节码指令,本地方法栈执行native方法

=> 为了保证线程中的局部变量不被别的线程访问,虚拟机栈和本地方法栈都是线程私有的。

【典中典】 并行&&并发、同步&&异步

并发和并行的区别

  • 并行:多个作业在同一时刻执行
  • 并发:多个作业在同一时间段内执行

同步和异步的区别

  • 同步:发出一个调用后,等待结果返回再执行之后的操作
  • 异步:调用发出后,不等待结果的返回

理解线程安全和不安全

  • 线程安全:在多线程环境下,程序能够正确地处理并发访问共享资源的能力。如果一个程序在多线程环境下能够保证数据的正确性和一致性,那么它就是线程安全的。
  • 线程不安全:在多线程环境下,程序不能正确地处理并发访问共享资源的情况。在多线程环境下,如果多个线程同时访问同一个共享资源,而且没有进行同步控制,那么就可能会出现数据竞争、死锁等问题,导致程序出现错误或崩溃。

线程的生命周期

在Thread源码中,线程的状态被分为了NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED六种状态。

public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,

/**
* 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,

/**
* 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,

/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,

/**
* 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:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,

/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
  • NEW:初始状态,线程被创建出来,尚未调用start()方法
  • RUNNABLE:运行状态,线程调用了start()方法,等待运行的状态
  • BLOCKED:阻塞状态,等待锁释放
  • WAITING:等待状态,表示该线程需要等待其他线程发出 通知或中断 操作
  • TIME_WAITING:超时等待状态,可以在指定时间后自行返回,而不是像WAITING状态那样一直等待
  • TERMINATE:终止状态,表示该线程已经运行结束

PS:整个线程的生命周期中,线程并不是固定处于某一个状态,而是随着程序的执行在不同的状态之间切换的

Java线程状态切换图

从上图可以看出,线程创建之后处于NEW状态,在调用start()方法之后开始执行,此时线程处于READY状态,在获得CPU时间片之后就处于RUNNING状态。

为什么JVM没有区分RUNNING和READY两种状态?

Thread#state注释原话:These states are virtual machine states which do not reflect any operating system thread states.

RUNNABLE状态的注释: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.

(https://www.zhihu.com/question/56494969/answer/154053599 By Dawell)现在的时分(time-sharing)多任务(multi-task)操作系统架构通常都是用所谓的“时间分片(time quantum or time slice)”方式进行抢占式(preemptive)轮转调度(round-robin式)。这个时间分片通常是很小的,一个线程一次最多只能在 cpu 上运行比如10-20ms 的时间(此时处于 running 状态),也即大概只有0.01秒这一量级,时间片用后就要被切换下来放入调度队列的末尾等待再次调度。(也即回到 ready 状态) 通常,Java的线程状态是服务于监控的,如果线程切换得是如此之快,那么区分 ready 与 running 就没什么太大意义了。

简单来说就是这两个状态之间切换过于频繁,时间太短,区分这两种状态没有多大意义。

当线程执行wait()方法之后,线程就进入WAITING状态,进入等待状态的线程需要依靠其他线程的通知才能够返回RUNNABLE状态。

TIME_WAITING状态相当于在WAITING状态的基础上增加了超时限制。在超时时间结束后,线程自行返回到RUNNABLE状态。

在线程进入同步代码方法/块或者调用wait()方法(被notify())重新进入同步代码方法/块,但是此时锁被其他线程占有,那么这个时候线程将进入BLOCKED状态。

线程执行完run()方法之后将会进入到TERMINATED状态。

参考文章

  • https://javaguide.cn/java/concurrent/java-concurrent-questions-01.html
  • https://blog.csdn.net/TZ845195485/article/details/109211695
  • https://mp.weixin.qq.com/s/UOrXql_LhOD8dhTq_EPI0w