Java高并发编程-初识多线程

没有强大理论支撑的代码,都是自娱自乐,架构师带你学习多线程的原理到实战项目中的高并发解决方案,闲暇之余,分享技术干货,和喜爱技术的coder们一起交流,互相学习进步

线程介绍

相对于计算机而言,每一个任务就是一个进程,每一个进程至少有一个线程,线程可以理解为轻量级进程,计算机操作系统一般不止一个线程运行,当启动了一个JVM时,就会创建一个进程,JVM进程会派生或创建多个线程,多个进程或线程可以并行执行

线程生命周期

每一个线程都有自己的局部变量表、程序计数器以及生命周期等,理解线程的生命周期,在日常的开发工作中会避免很多坑,线程生命周期分为5个阶段:NEW、RUNNABLE、RUNNING、BLOCKED、TERMINATED,下面对线程的五种状态详细说明

 

1. 线程的NEW状态(新建)

创建一个Thread对象时,线程进入NEW状态,准确的讲,此时并没有在JVM中创建线程,与普通的java对象没有区别,需要通过调用start方法,使线程进入RUNNABLE状态

 

2. 线程的RUNNABLE状态(就绪)

Thread线程对象调用start方法,会在JVM中创建一个线程,此时线程进入RUNNABLE状态,RUNNABLE是中间状态,只是获得了线程执行的资格,需要获取CPU调度执行权才会进入RUNNING状态,RUNNABLE状态只能意外终止或进入RUNNING状态

 

3. 线程的RUNNING状态(运行)

当线程获得了CPU调度执行权,线程进入RUNNING状态,此时才会执行线程中的业务逻辑,RUNNING状态的线程可以发生以下状态转换:

(1) 调用sleep,或者wait方法添加到waitSet中,进入BLOCKED状态

(2) IO阻塞,进入BLOCKED状态

(3) 获取锁,加入到阻塞队列,进入BLOCKED状态

(4) 由于CPU调度器轮询,使该线程放弃执行,进入RUNNABEL状态

(5) 线程主动调用yield方法,放弃线程执行,进入RUNNABEL状态

(6) 调用JDK不推荐的stop方法或者通过判断标识,进入TERNIMATED状态

 

4. 线程的BLOCKED状态(阻塞)

线程进入阻塞状态的原因:IO阻塞、获取锁进入阻塞队列、调用sleep方法、wait方法加入到waitSet,BLOCKED状态的线程可以发生以下状态转换:

(1) IO阻塞完成,进入RUNNABLE状态

(2) 线程在阻塞过程中,其他线程调用了interrupt方法,导致线程被打断,进入RUNNABLE状态

(3) 线程调用的sleep休眠时间完成,进入RUNNABLE状态

(4) 调查调用wait方法后,通过notify或notifyAll方法唤醒现场,进入RUNNABLE状态

(5) 获取资源锁,进入RUNNABLE状态

(6) 调用JDK不推荐的stop方法或者通过判断标识,进入TERNIMATED状态

 

5. 线程的TERMINATED状态(销毁)

TERMINATED状态是线程的最终状态,线程进入TERMINATED状态,意味着线程生命周期结束,不会转换为其他状态,线程进入TERMINATED的条件:

(1) 线程正常执行完成,线程结束

(2) 线程运行出错意外结束

(3) JVM Crash导致所有线程结束

深入了解start方法

private volatile int threadStatus = 0;

 

public synchronized void start() {

if (threadStatus != 0)

throw new IllegalThreadStateException();

group.add(this);

boolean started = false;

try {

start0();

started = true;

finally {

try {

if (!started) {

group.threadStartFailed(this);

}

catch (Throwable ignore) {

}

}

}

 

private native void start0();

 

@Override

public void run() {

if (target != null) {

target.run();

}

}

 

结合以上源码,总结一下:

(1) 创建Thread对象,threadStatus为线程的状态标识,初始状态为0,该状态在JVM中修改,因为start0是native修饰的,所以start0的实现是C++方法,所以有一点做得非常友好,threadStatus是volatile修饰的内存可见,java可以获取threadStatus的最新状态

 

(2) 线程调用start方法后,线程状态切换,threadStatus会修改,再次调用start方法,threadStatus不等于0,会抛
IllegalThreadStateException异常

 

(3) 这里讲一下针对不同线程状态的两个测试用例,就不附带代码了,自己多敲敲

(1) 创建一个Thread类,重写run方法,方法中休眠5秒,重复调用start方法,本次测试线程运行过程重复启动

(2) 创建一个Thread类,重写run方法,方法中休眠5秒,调用start方法,调用sleep方法,休眠10秒,再调用一次start方法,本次测试线程生命周期结束后,再次启动

Runnable接口详解

创建线程的方式

以前我们面试经常会被问到,创建线程的方式有哪些?通常会回答,继承Thread类、实现Runnable接口,这种说法是不严谨的,准确的说,创建线程的方式只有一种,就是构造Thread类,实现线程执行单元的方式有2种:

(1) 重写Thread类的run方法

(2) 实现Runnable接口的run方法,并且将Runnable作为构造Thread类的参数

以上两种方式的不同点在于,重写Thread类的run方法,多个Thread的run方法是独立的,不能作为共享单元,而同一个Runnable实例,可以构造不同的Thread,也就是说通过实现Runnable接口可以接口共享资源的问题(线程安全问题暂时不考虑,后面会讲解)

 

设计模式应用

1. 模板设计模式

Thread中的start方法和run方法应用了模板设计模式,父类定义算法结构,行为由父类控制,不允许被重写,子类实现需要的逻辑细节

2. 策略设计模式

通过将Runnable实例作为参数构造Thread,应用到了策略设计模式,将线程控制和业务逻辑分离,Thread只负责控制线程,Runnable作为策略接口,只负责执行业务单元

你可能感兴趣的:(java,多线程,高并发,源码,面试)