大家好,我是
方圆
,这篇博客儿主要列举一些并发编程的基础知识
文章目录
1. Java运行时默认开启线程
// 查看当前所有线程
for (Map.Entry<Thread, StackTraceElement[]> threadEntry : Thread.getAllStackTraces().entrySet()) {
System.out.println(threadEntry.getKey());
}
控制台输出如下
,除了main主线程外,还有其他五个线程,下面逐条解释
Monitor Ctrl-Break:
这个是IDEA开启的线程,监控Ctrl-Break中断信号Attach Listener:
Attach Listener线程是负责接收到外部的命令,而对该命令进行执行的并且把结果返回给发送者,通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version、jmap、jstack等等Signal Dispatcher:
前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作Finalizer:
这个线程也是在main线程之后创建的,其在Java中的优先级为8,主要用于在垃圾收集前,调用对象的finalize()方法;关于Finalizer线程的几点:
- 只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行
- 该线程也是daemon线程,不管该线程有没有执行完finalize()方法,JVM都会退出
- JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if ("Finalizer".equals(thread.getName())) {
// 查看Finalizer线程的优先级
System.out.println("Priority: " + thread.getPriority());
}
}
// 控制台显示如下:
Priority: 8
注: JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难
Reference Handler:
JVM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题
for (Thread thread : Thread.getAllStackTraces().keySet()) {
// 查看Reference Handler的线程优先级
if ("Reference Handler".equals(thread.getName())) {
System.out.println("Reference Handler Priority: " + thread.getPriority());
}
}
// 控制台显示如下:
Reference Handler Priority: 10
2. 线程的六种状态
在Thread类源码中,State枚举
,状态如下
NEW:
已经建立尚未启动的线程RUNNABLE:
在执行中的线程BLOCKED:
阻塞状态,线程在等待锁时为该状态WAITING:
等待状态,Object.wait(),Thread.join(),LockSupport.park这三个方法无参调用会使线程进入该状态TIMED_WAITING:
超时等待状态。等待另一个线程到达指定时间
的等待状态,Object.wait()和Thread.join()方法的有参调用,Thread.sleep(),LockSupport.parkNanos,LockSupport.parkUntil方法会使线程进入该状态TERMINATED:
终止状态,代表线程已经完全执行完了
3. 线程池的五种状态
在ThreadPoolExecutor
类的源码中,有一段如下注释,读起来也很容易
* The runState provides the main lifecycle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
- 完整的线程池
生命周期
由以下五个状态控制 RUNNING:
能接受新的任务,并且也能处理阻塞队列中的任务SHUTDOWN:
不再接收新提交的任务,但仍然处理存量任务STOP:
不再接受新提交的任务,也不处理存量任务并且会打断当前执行的任务TIDYING:
所有任务都终止TERMINATED:
执行terminated()方法执行完,进入该状态
4. wait() 和 sleep()的区别
原理不同
。wait()是Object类的方法,用于线程通信。sleep()是Thread的静态方法,用于控制流程,让线程"睡觉",把执行的机会让给其他线程使用的区域不同
。wait()用于同步方法或同步代码块儿中。sleep()随处都可睡对锁的处理也不同
。wait()的时候就会把锁给释放了,而sleep()则会和锁一起睡觉,不会释放锁
5. synchronized 和 Lock 的区别
- synchronized 是内置的关键字;Lock是java的接口
- synchronized 可重入,不可中断,非公平;Lock 可重入,可判断,可公平
- synchronized 无法判断锁的状态;Lock可以判断锁的状态
- synchronized 会自动释放锁;Lock需要手动释放锁
- synchronized 会当线程获得锁并且阻塞的话,其他线程会一直等待;Lock不一定会一直等待下去,可以尝试获得锁(
tryLock()方法
),获取不到,就不再等待
- ReentrantLock 构造函数源码
6. 查看死锁问题的堆栈信息
-
死锁发生的时候,我们可以通过
jps
和jstack
命令来查看堆栈信息
-
在堆栈信息中,我们可以发现
Found 1 deadlock
的死锁提示,以及发生死锁的具体信息
加油儿!