Java中线程状态与线程死锁


1. 什么是线程

线程(英语:thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

1.1 线程与进程

最近看到一个通俗易懂的解释,所以拿过来整理一下,原地址:进程与线程的一个简单解释

  1. 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
  2. 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
  3. 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态
  4. 一个车间里,可以有很多工人。他们协同完成一个任务。
  5. 线程就好比车间里的工人。一个进程可以包括多个线程。
  6. 车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
  7. 可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
  8. 一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫”互斥锁”(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
  9. 还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
  10. 这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做”信号量”(Semaphore),用来保证多个线程不会互相冲突。不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
  11. 操作系统的设计,因此可以归结为三点:
    • 1)以多进程形式,允许多个任务同时运行;
    • 2)以多线程形式,允许单个任务分成不同的部分运行;
    • 3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

2. 线程有哪些状态

状态 说明
new
runnable Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
blocked 表示线程阻塞于锁。
waiting 进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
timed_waiting 该状态不同于WAITING,它可以在指定的时间后自行返回。
terminated 表示该线程已经执行完毕。

3. 线程的状态之间是这么切换的

4. 什么是线程死锁

线程死锁是指由于两个或多个线程相互持有对方所需的资源,导致这些线程处于等待状态,无法往前继续执行。

5. 模拟线程死锁

代码模拟线程死锁

/**
 * @Author haonan.bian
 * @Description 线程死锁模拟
 * @Date 2020-01-31 12:22
 **/
public class DeaLock {
    private  static Object lock1 = new Object();
    private  static Object lock2 = new Object();
    public static void main(String[] args) {
        System.out.println("main");
        new Thread(()->{
            synchronized (lock1){
                try{
                    System.out.println(Thread.currentThread().getName()+":得到lock1");
                    Thread.sleep(5000L);
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println(Thread.currentThread().getName()+":得到lock2");
                }
            }
        },"线程1").start();

        new Thread(()->{
            synchronized (lock2){
                try{
                    System.out.println(Thread.currentThread().getName()+":得到lock2");
                    Thread.sleep(5000L);
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (lock1){
                    System.out.println(Thread.currentThread().getName()+":得到lock1");
                }
            }
        },"线程2").start();

    }
}
  • 执行代码

[root@node1 opt]# java -jar MyJava_JVM-1.0-SNAPSHOT.jar
main
线程1:得到lock1
线程2:得到lock2
  • 使用 jstack 检查线程死锁
[root@node1 ~]# jstack -l 21148
2020-01-31 13:34:36
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fdca8001000 nid=0x5335 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fdcd0009800 nid=0x529d waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"线程2" #9 prio=5 os_prio=0 tid=0x00007fdcd0138000 nid=0x52a7 waiting for monitor entry [0x00007fdcc06a6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.hnbian.memory.structure.DeaLock.lambda$main$1(DeaLock.java:36)
    - waiting to lock <0x00000000e3469600> (a java.lang.Object)
    - locked <0x00000000e3469610> (a java.lang.Object)
    at com.hnbian.memory.structure.DeaLock$$Lambda$2/250421012.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
    - None

"线程1" #8 prio=5 os_prio=0 tid=0x00007fdcd0136800 nid=0x52a6 waiting for monitor entry [0x00007fdcc07a7000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.hnbian.memory.structure.DeaLock.lambda$main$0(DeaLock.java:22)
    - waiting to lock <0x00000000e3469610> (a java.lang.Object)
    - locked <0x00000000e3469600> (a java.lang.Object)
    at com.hnbian.memory.structure.DeaLock$$Lambda$1/245257410.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
    - None

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fdcd00db000 nid=0x52a4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fdcd00d8000 nid=0x52a3 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fdcd00d5800 nid=0x52a2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fdcd00d4000 nid=0x52a1 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fdcd00a1000 nid=0x52a0 in Object.wait() [0x00007fdcc0dad000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000e3408ed0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
    - locked <0x00000000e3408ed0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

   Locked ownable synchronizers:
    - None

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fdcd009e800 nid=0x529f in Object.wait() [0x00007fdcc0eae000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000e3406bf8> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x00000000e3406bf8> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
    - None

"VM Thread" os_prio=0 tid=0x00007fdcd0095000 nid=0x529e runnable

"VM Periodic Task Thread" os_prio=0 tid=0x00007fdcd00e0000 nid=0x52a5 waiting on condition

JNI global references: 310


Found one Java-level deadlock: # 发现线程死锁
=============================
"线程2":
  waiting to lock monitor 0x00007fdcb40062c8 (object 0x00000000e3469600, a java.lang.Object),
  which is held by "线程1"
"线程1":
  waiting to lock monitor 0x00007fdcb40038d8 (object 0x00000000e3469610, a java.lang.Object),
  which is held by "线程2"

Java stack information for the threads listed above:
===================================================
"线程2":
    at com.hnbian.memory.structure.DeaLock.lambda$main$1(DeaLock.java:36) # 标注线程死锁行数
    - waiting to lock <0x00000000e3469600> (a java.lang.Object)
    - locked <0x00000000e3469610> (a java.lang.Object)
    at com.hnbian.memory.structure.DeaLock$$Lambda$2/250421012.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
"线程1":
    at com.hnbian.memory.structure.DeaLock.lambda$main$0(DeaLock.java:22) # 标注线程死锁行数
    - waiting to lock <0x00000000e3469610> (a java.lang.Object)
    - locked <0x00000000e3469600> (a java.lang.Object)
    at com.hnbian.memory.structure.DeaLock$$Lambda$1/245257410.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
Kafka总结(五)Kafka 命令行详解上、topic、producer、consumer相关命令 Kafka总结(五)Kafka 命令行详解上、topic、producer、consumer相关命令
脚本列表 序号 脚本 功能 1 kafka-server-start.sh 启动kafka服务 2 kafka-server-stop.sh 停止kafka服务 3 kafka-topics.sh topic管理脚本
2020-02-16
下一篇 
Kafka总结(四)kafka auto.offset.reset参数说明 Kafka总结(四)kafka auto.offset.reset参数说明
1. 参数说明 当各分区下有已提交的offset时(默认使用latest) 参数 说明 earliest 从提交的offset开始消费;无提交的offset时,从头开始消费 latest 从提交的offset开始消费;无提
2020-02-14
  目录