技术问答集锦(二)

4 如何经过JDK命令,分析排查死锁、死循环?

top 命令:显示当前的活动进程,默认它是按消耗 CPU 的厉害程度进行排序,每5秒钟刷新一次列表,你也能够选择不一样的排序方式,例如 m 是按内存占用方式进行排序的快捷键。java

命令:top -Hp pid算法

top -Hp pid

能够实时的跟踪并获取指定进程中最耗cpu的线程。再用 jstack方法提取到对应的线程堆栈信息缓存

jps 命令:安全

jps用来查看JVM里面全部进程的具体状态, 包括进程ID,进程启动的路径等等。网络

jstack 命令:数据结构

  1. 查看java程序崩溃生成core文件,得到core文件的java stack和native stack的信息;
  2. 查看正在运行的java程序的java stack和native stack的信息:a) 查看运行的java程序呈现hung的状态;b) 跟踪Java的调用栈,剖析程序。

线程的状态分析:多线程

Runnable:该状态 表示线程具有全部运行条件,在运行队列中准备操做系统的调度,或者正在运行并发

Wait on condition:该状态出如今线程等待某个条件的发生。具体是什么缘由,能够结合 stacktrace来分析。最多见的状况是线程在等待网络的读写;另一种出现 Wait on condition的常见状况是 该线程在 sleep,等待 sleep的时间到了时候,将被唤醒异步

Waiting for monitor entry 和 in Object.wait():在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是Java中用以实现线程之间的互斥与协做的主要手段,它能够当作是对象或者 Class的锁。每个对象都有,也仅有一个 monitorjvm

jinfo 命令:

jinfo可观察运行中的java程序的运行环境参数:参数包括Java System属性和JVM命令行参数;也可从core文件里面知道崩溃的Java应用程序的配置信息。

jstat 命令:

jstat利用了JVM内建的指令 对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收情况的监控等等。

jmap 命令:

观察运行中的jvm物理内存的占用状况,包括Heap size, Perm size等等。

4.1 Java死循环分析

  • 查看进程ID:top 或者 jps

  • 按CPU使用率展现当前JAVA程序的全部线程:top -Hp 3230

    top -Hp 3230

其实这个地方按CPU的使用率来断定还不太好理解,以运行时间来断定可能更能说明问题些。

  • 将运行时间最长的 本地线程ID(3244)转成16进制为0xcac

  • 生成线程堆栈日志文件 jstack -l 3230 > jstack.log

  • 打开堆栈日志 搜索“0xcac”

    堆栈日志搜索“0xcac”

  • 很容易的就找到了无限循环的调用线程堆栈。

4.2 Java死锁分析

在多线程程序的编写中,若是不适当的运用同步机制,则有可能形成程序的死锁,常常表现为程序的停顿,或者再也不响应用户的请求。 好比在下面这个示例中,是个较为典型的死锁状况:

较为典型的死锁状况

5 Java中j.u.c包原理实现及CAS算法?

5.1 Java的多线程同步机制

在现代的多处理器系统中,提升程序的并行执行能力是 有效利用 CPU 资源的关键。为了有效协调多线程间的并发访问,必须采用适当的同步机制来协调竞争。当前经常使用的多线程同步机制能够分为下面三种类型:

volatile 变量:轻量级多线程同步机制,不会引发上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。

CAS 原子指令:轻量级多线程同步机制,不会引发上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。

内部锁和显式锁:重量级多线程同步机制,可能会引发上下文切换和线程调度,它同时提供内存可见性和原子性。

在这里,CAS 指的是现代 CPU 普遍支持的一种对内存中的共享数据进行操做的一种特殊指令。这个指令会对内存中的共享数据作原子的读写操做。简单介绍一下这个指令的操做过程:首先,CPU 会将内存中将要被更改的数据与指望的值作比较。而后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。不然便不作操做。最后,CPU 会将旧的数值返回。这一系列的操做是原子的。它们虽然看似复杂,但倒是 Java 5 并发机制优于原有锁机制的根本。简单来讲,CAS 的含义是“我认为原有的值应该是什么,若是是,则将原有的值更新为新值,不然不作修改,并告诉我原来的值是多少”。

在轻度到中度的争用状况下,非阻塞算法的性能会超越阻塞算法,由于 CAS 的多数时间都在第一次尝试时就成功,而发生争用时的开销也不涉及线程挂起和上下文切换,只多了几个循环迭代。没有争用的 CAS 要比没有争用的锁便宜得多(这句话确定是真的,由于没有争用的锁涉及 CAS 加上额外的处理),而争用的 CAS 比争用的锁获取涉及更短的延迟。

5.2 Java中j.u.c包原理实现

Java的CAS会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读-改-写操做,这是在多处理器中实现同步的关键(从本质上来讲,可以支持原子性读-改-写指令的计算机器,是顺序计算图灵机的异步等价机器,所以 任何现代的多处理器都会去支持某种能对内存执行原子性读-改-写操做的原子指令)。同时,volatile变量的读/写和CAS能够实现线程之间的通讯。把这些特性整合在一块儿,就造成了整个concurrent包得以实现的基石。若是咱们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式:

  1. 首先,声明共享变量为volatile;
  2. 而后,使用CAS的原子条件更新来实现线程之间的同步;
  3. 同时,配合以volatile的读/写和CAS所具备的volatile读和写的内存语义来实现线程之间的通讯。

AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类来实现的。从总体来看,concurrent包的实现示意图以下:

concurrent包实现示意图

6 有锁与无锁的本质区别?

有锁、无锁的本质都是在解决并发状况下竞态资源的线程安全问题。无锁只是把“排他性”进一步的弱化,以提升并发量,最大限度的使用CPU

无锁只会在CPU控制权切换的等待,而有锁会在“锁释放”、“CPU控制权切换”两种状况下的等待。

最终无锁状况下,CPU使用率上不去,吞吐量降低,就受系统资源限制了。即便线程量增长,无非增长的是线程CPU控制权的切换成本,统一受CPU的控制调度,出现的也只能是等待了。

7 volatile关键字是不是线程安全的?

volatile关键字仅保证两点:

  1. volatile变量线程之间可见性;
  2. 禁止针对volatile变量操做指令重排序;

但最重要一点没有保证,关系到线程安全,就是Volatile变量操做指令的不保证原子性问题

什么是原子操做:

多个线程执行一个操做时,其中任何一个线程要么彻底执行完此操做的步骤,要么就没有执行此操做的任何步骤,那么认为这个操做才是原子的。

关于指令重排序的问题,在单线程中,重排序的先后只要不影响JVM执行结果,JVM能够运行指令重排序

指令重排序的意义在于:

JVM可以根据处理器特性(CPU多级缓存系统、多核处理器等)适当的从新排序机器指令,使机器指令更符合CPU的执行特色,最大限度的发挥机器性能。

可是 在指令重排序在多线程的状况下,重排序的先后结果,可能不会一致了。这里致使结果不一致主要是线程安全的问题,即便指令不重排序,也会存在线程安全问题。这也就是原子性问题、或者没有加锁的问题

相关文章
相关标签/搜索