Java多线程基础
### 线程的出现
- 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快
- 在多核CPU中,利用多线程可以实现真正意义的并行执行(单核cpu中线程是通过cpu时间片不断切换执行的,在任意时刻只会有一个线程会被cpu调度)
- 在一个应用进程中,会存在多个同时执行的任务,如果其中一个任务被阻塞,将会导致其他任务也会被阻塞。通过对不同任务创建不同的线程去处理,可以提升程序的实时性
线程生命周期(6种状态)
NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED(详情见Thread源码)
- NEW:初始状态,线程,但是还没有调用线程的start方法
- RUNNABLE:运行状态,JAVA把操作系统中线程的就绪和运行两种状态统一称为“运行中”
- BLOCKED:阻塞状态,表示线程进入等待状态,线程因为某种原因放弃了CPU使用权,阻塞分以下几种情况
- 等待阻塞:运行的线程执行wait方法(只有获得锁,才能执行wait方法),jvm会把当前的线程放入到等待队列
- 同步阻塞:运行的线程在获取对象的同步锁时,如果该同步锁被其他线程占用了,那么jvm会把当前线程放入到锁池中
- 其他阻塞:运行的线程执行了Thread.sleep或者join方法,或者
发出了IO请求
时,jvm会帮当前线程设置为阻塞状态,当sleep结束、join线程终止、io处理完毕,线程恢复 - TIME_WAITING:超时等待状态,超时后自动返回
- TERMINATED:终止状态,表示当前线程执行完毕
1 | /** |
查看线程的状态及jvm内存监控可以参考以下命令:
jps:查看本机的Java中进程信息
jstack:打印线程的栈信息,制作线程Dump(jstack pid<进程id>)
jmap:打印内存映射,制作堆Dump(jmap -dump:format-b,file=保存文件路径)
jstat:性能监控工具
jhat:内存分析工具 jhat -port file<dump文件>
jconsole:简易的可视化控制台
jvisualvm:功能强大的控制台
线程的创建
- 继承Thread类(本质是对Runnable接口的实现)
- 实现Runnable接口
- 使用ExecutorService
- Callable、Future(带返回值)
启动线程的唯一方法就是通过Thread类的start方法(srart方法是一个native方法,它会启动一个新的线程,并执行run方法)
1 | /** Thread class source code */ |
jvm线程创建、启动源码
打开OpenJDK / jdk8 / jdk8 / jdk源码,找到文件->src/share/native/java/lang/Thread.c @ 2362:00cd9dc3c2b5,可以看到java中Thread类中调用的本地方法,在虚拟机jvm.cpp文件中的定义,以及映射关系,本地方法通过JNI技术暴露给外部调用。
Thread.c文件源码:
1 |
|
/src/share/vm/prims/jvm.cpp源码(线程启动):
1 | JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) |
从源码native_thread = new JavaThread(&thread_entry, sz);可以看出,本地线程native_thread是通过创建JAVA线程来创建的,通过Thread::start(native_thread)来启动线程,最终通过操作系统来创建线程。
/src/share/vm/runtime/thread.cpp源码:
1 | void Thread::start(Thread* thread) { |
jvm中断线程方法
/src/share/vm/prims/jvm.cpp源码,jvm最后调用Thread.cpp中的is_interrupted方法去中断某个线程
1 | JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread)) |
/src/share/vm/runtime/thread.cpp源码,最终是调用操作系统的is_interrupted方法中断线程
1 | void Thread::interrupt(Thread* thread) { |
/src/os/linux/vm/os_linux.cpp源代码(这里已linux操作系统平台为例),通过调用OSThread类的set_interrupted(true),将成员变量_interrupted设置为true,来设置线程中断,默认为false
1 | void os::interrupt(Thread* thread) { |
/src/share/vm/runtime/osThread.hpp源代码
1 | class OSThread: public CHeapObj<mtThread> { |
线程Interrupte之后调用Thread.interrupted()复位
Thread.java
1 | Thread.interrupted(); // 复位,回到初始状态 |
中断一个处于阻塞状态(sleep/wait/join/…)的线程,会先复位,再抛出异常(为什么会抛出异常可以参考jvm.cpp文件中的JVM_Sleep方法)
1 | JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis)) |
实际应用
- 线程池
- zookeeper责任链,异步化任务
- 跑批脚本、对账文件
并发编程相关文章持续更新中…