Java 多线程

Java 多线程

什么是进程和线程。

我们启动一个mian函数,其实是启动了一个JVM的进程,而main函数所在的线程就是这个进程中的一个,也叫主线程。

进程是一个系统运行程序的基本单位。线程是一个比进程更小的执行单位,一个进程可以产生多个线程,线程会比进程之间进行切换工作小很多,因为线程有共享内存,进程没有他们需要保存和恢复整个进程的状态。(这里的切换是线程的暂停切换到另外一个线程)

用户级线程和内核线程

​ 用户线程:由用户空间程序管理和调度的线程,运行在用户空间。(专门给应用程序使用),JDK1.2版本之前版本使用。JVM自己模拟了多线程的运行。

​ 内核线程: 有操作系统管理和调度的线程,运行在内核空间(只有内核时程序可以访问)jdk版本大于等于 1.2,也就是说JVM直接使用操作系统图原生内核线程来实现的。

Java线程与操作系统的线程关系是一对一也就说,一个Java线程的线程模型是对应一个系统内核线程。

线程模型主要有三种

一对多,多对多、一对一

从JVM角度说线程 1.8

线程共享区域

主要存放我们的对象, 基本所有的创建的对象都会存放到这里,也是我们垃圾回收机主要回收的地方。

元空间(以前的方法区)

元空间主要存储了描述类结构和组成部分的各种信息,这些信息称呼为元信息。比如类的访问修饰符,方法的访问修饰符,方法名字,方法的返回类型, 方法的参数等,使用的是本地内存。

类的加载是懒加载,只有在类的第一次使用的时候才会加载,然后以后再用的时候就去元空间里获取, 比如创建对象。

线程私有

虚拟机栈

当我们执行一个java方法的时候就会把一个栈帧放到jvm栈中,这个栈帧包含了局部变量表(方法参数,局部变量),操作数栈方法返回地址(执行完返回下一条命令的地址),动态链接(方法的引用)

本地方法栈

​ 本地方法栈和jvm栈是一样的,唯一区别的就是本地方法栈是为用来操作系统的方法的。

程序计数器

​ 字节码解析器通过改变程序计数器依次来读取指令,实现代码的流程龙之如顺序执行、选择、循环、异常处理。

​ 在多线程记录当前线程执行的位置,在线程切换知道该线程上次运行到哪里了。

本地内存

元空间

​ 上面有介绍

直接内存

​ 直接内存是使用NIO包中的ByteBuffer类来操作的一种内存,他不受java堆的管理,也不受jvm gc管理,直接内存的分配和释放由操作系统实现。

​ 它与Java堆外的物理内存直接映射,可以不受jvm 堆大小的限制。

​ 直接内存可以通过0拷贝的技术提高I\O的操作性能,因为它的数据可以在Java堆和本地内存之间的传输。

​ 在内存释放方面是比较延迟的,当我们执行claer方法只是清除了ByteBuffer的状态,并没有释放真正的内存要等操作系统来释放。

NIO的优化就是比传统IO少了一步操作,就是少了把堆外内存拷贝到堆内内存。

NIO

​ 使用ByteBuffer进行数据的读写,将数据写到直接缓冲区,这个直接缓冲区(Direct ByteBuffer)对应着堆外内存。(第一次拷贝,从用户空间到内核空间)

​ 写时调用force()方法刷新到文件系统的穿冲区,然后等操作系统刷新到硬盘(这是第二次拷贝)

​ 硬盘读取数据到文件系统的缓冲区,然后通过通道传输到内核空间,最终拷贝到用户空间的缓冲区。

并发和并行

并发:两个及以上的任务在同一时间段执行

并行:两个及以上的任务同一时刻执行。

同步和异步

同步:等待后响应后才能继续执行。

异步:不需要等待响应就可以继续执行。

为什么使用多线程?

**计算机层面:**线程的切换比进程小很多,线程要比进程轻量级很多。多核CPU意味着可以有多个线程。

**高并发场景层面来说:**多线程是高并发层面的基础,利用好多线程的机制可以大大的提高系统整体的并发能力以及性能。

在计算机底层CPU来说: 在单核时代多线程主要为了提高单线程利用CPU和IO的效率,CPU 和 IO 设备只有一个在运行,而在使用多线程就不会。

​ 在多核多线程时代,假如我们创建一个线程,都会有一个CPU核心执行,如果我们多线程,就会分配到多个核心

单核CPU上运行多线程效率一定高吗

一般来说有2种类型线程,分别为CPU密集型和IO密集型,CPU密集型需要占用大量的CPU资源,比如执行计算和逻辑处理。这么来说会慢(因为频繁的切换线程导致了系统开销),IO密集型主要进行输入输出操作,如读写文件、网络通信、等待IO设备响应。这么来着就会快(多个线程可以利用等待IO的时间,提高了效率)。

线程的生命周期和状态

new:初始状态,线程被创建出来但是没有调用start()

ready: 可运行状态。执行了start方法

running:运行状态,CPU时间片给可运行状态运行权利了。

blocked: 堵塞状态,需要等待锁的释放。

​ synchronized 代码块或者被wait唤醒重新进入synchronized方法等待锁。

waiting:表示当前线程需要等待其他线程做出一些特定的动作(通知或中断) 执行了wait方法

time_wating:超时等待,可以在指定时间内自行返回,而不是像waiting那样一直等地啊。执行了wait(time)(会被notify叫醒) sleep(time)会进入这个状态

Terminated: 终止状态,表示该线程已经运行完毕。执行完run方法

线程的生命周期是随着代码在不同的状态切换的。

上下文切换

上下文切换就是线程当前THerad的信息,留着下次这个Thread恢复使用。

当我们主动让出CPU(Sleep,wait)和我们时间片用完了(操作系统怕一个线程时间太久,其他线程进程饿死)或者 堵塞类型的系统中断,比如请求IO、线程被堵塞就会进行上下文切换。

sleep和wait的区别

共同点,都可以暂停线程的执行,只是他们的关注点不一样。

sleep没有释放锁,wait会释放。

slepp用于暂停执行, wait用于线程之间的交互/通信。

sleep会自动苏醒, wait需要其他线程叫醒,wait(long time)也可以等待超时苏醒, 然后重新获取锁,。

sleep是Thread类的静态方法,wait是Object的本地方法.

为什么wait方法在Object

因为wait关注点是在有对象锁的线程实现等待,会自动释放当前线程锁占用的锁进入waiting状态,因为,每个对象都有对象锁。

为什么sleep方法在Thread

因为sleep是让当前线程暂停执行,不需要锁。

可以直接调用 Thread 类的 run 方法吗?

可以,但是只会当做一个普通方法执行, 因为start方法会进入可执行状态,等待CPU的使用权力,而执行运行run方法只会看作一个普通线程。

你可能感兴趣的:(java,开发语言)