什么是多线程?多线程API详解

目录java

一、多线程基础编程

1.1 什么是线程安全

1.2 建立线程网络

1.3 线程的生命周期多线程

1.3.1 线程的NEW状态并发

1.3.2 线程的RUNNABLE状态高并发

1.3.3 线程的RUNNING状态spa

1.3.4 线程的BLOCKED状态操作系统

1.3.5 线程的TERMINATED状态线程

1.4 Thread模拟营业厅叫号机程序

1.5 守护线程

二、Thread APA的信息介绍

2.1 线程sleep

2.2 线程yield

2.3 设置线程优先级

2.4 获取线程ID

2.5 获取当前线程

2.6 设置线程上下文加载器

2.7 线程interrupt

2.8 线程join

2.9 关闭线程

2.9.1 正常关闭

2.9.2 异常退出

2.9.3 进程假死

 


1.1 什么是线程

线程是操做系统可以进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运做单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中能够并发多个线程,每一个线程执行不一样的任务。

特色:轻型实体、独立调度和分派的基本单位、可并发执行、共享进程资源

1.2 建立线程

建立线程的两种方法:1 实现Runnable接口并从写run方法;2 集成Thread类并从新run方法。

实现Runnable接口:

image.png

 

集成Thread类:

image.png

 

1.3 线程的生命周期

线程的什么周期大体能够分为五个阶段:一、new(新建) 二、Runnable(就绪) 三、Running(运行) 四、Blocked(阻塞) 五、Terminated(结束)

1.3.1 线程的NEW状态

当咱们使用关键字new建立一个Thread对象时,此时他并不处于执行状态,由于没有调用start的方法启动该线程,那么线程的状态为New状态。NEW状态只有经过start方法进入RUNNABLE(就绪状态)状态。

1.3.2 线程的RUNNABLE状态

线程对象进入RUNNABLE(就绪状态)必须调用start方法,那么此时才真正的在JVM进程中建立了一个线程,线程的运行与否都要由CPU的调度,那么咱们把这个中间转态成为可执行状态,就是已经就绪,可是没有真正的执行起来。

1.3.3 线程的RUNNING状态

一旦CPU经过轮询或者其余方式从任务可执行队列中选中了线程,那么此时他才能真正的执行本身的逻辑代码。在该状态下,线程能够发生以下状态切换:

1)直接进入TERMINATED状态,好比调用了JDK已经不推荐使用的stop方法或者判断某个逻辑标识。

2)进入BLOCKED状态,好比调用sleep,或者wait方法进入waitSet中

3)进入某个阻塞的IO操做,好比因网络数据的读写进入了BLOCKED状态

4)获取某个锁资源,从而加入到该锁的阻塞队列中而进入BLOCKED状态

5)因为CPU的调度器轮询是该线程放弃执行,进入RUNNABLE状态

5)线程主动yield(线程让步)方法,放弃CPU执行权,进入RUNNABLE状态

1.3.4 线程的BLOCKED状态

就是由1.3.3线程的RUNNING状态进入BLOCKED状态

在该状态下,线程能够发生以下状态切换:

1)直接进入TERMINATED状态,好比调用了JDK已经不推荐使用的stop方法或者判断某个逻辑标识。

2)线程阻塞的操做结束,进入到RUNNABLE状态

3)线程完成休眠,进入RUNNABLE状态

4)wait中的线程被其余线程的notify/notifyall唤醒,进入RANNABLE状态

1.3.5 线程的TERMINATED状态

线程的TERMINATED状态是一个线程的最终状态,该状态不会再切换到任何状态,进入此状态就意味着该线程的整个生命周期都结束了。

1.4 Thread模拟营业厅叫号机程序

程序以下:

image.png

 

1.5 守护线程

什么是守护线程:

JVM在没有一个非守护线程运行时,JVM将会退出。守护线程拥有本身结束本身生命周期的特性,而非守护线程没有。JVM的垃圾回收线程就是典型的守护线程,在垃圾回收结束后自动结束生命周期。

二、Thread APA的信息介绍

2.1 线程sleep

sleep是一个静态方法,其有两个重载方法,起重一个须要传入毫秒数,另外一个既须要毫秒也须要纳秒数。

使用Thread.sleep()方法就能把当前线程进入指定的时间进行休眠。

使用TimeUnit替代Thread.sleep

JDK1.5以后就引入了一个枚举TimeUnit,是对sleep的封装,咱们使用sleep要对时间进行换算,TimeUnit就省去了这一步骤。例如想让程序休眠2小时24分钟12秒44毫秒

TimeUnit.HOURS.sleep(2);  //小时

TimeUnit.MINUTES.sleep(24); //1分钟

TimeUnit.SECONDS.sleep(12);  //秒

TimeUnit.MILLISECONDS.sleep(44);  //毫秒

sleep和wait的区别:

1)sleep是Thread的方法,而wait是Object的方法

2)wait方法的执行必须在同步的方法中进行,而sleep则不须要

3)线程在同步方法中执行sleep方法时,并不会释放monitor锁,而wait方法则会释放monitor的锁

4)sleep方法短暂休眠以后会主动退出阻塞,而wait方法则须要被其余线程中断后才能退出

2.2 线程yield

yield方法属于启发式的方法,其会提醒调度器我愿意放弃当前CPU的资源,若是CPU的资源不紧张,则会忽略这种提醒。yield方法不是每次都生效的。

sleep和yield的区别:

sleep会致使当前线程暂停指定的时间,没有CPU的消耗,几乎百分百的完成了给定的时间休眠,而yield则不会。

yield只是对CPU的调度器的一个提示,若是CPU没有忽视,会致使上下文的切换。

2.3 设置线程优先级

线程的优先级比较高会优先获取CPU的调度,可是事实每每不会如你所愿,当CPU比较忙时,设置优先级可能会得到更多的CPU时间片,可是闲时优先级的高低几乎不会有任何做用。

setPriority() 设置优先级 getPriority() 获取当前线程的优先级

2.4 获取线程ID

public long getId() 获取线程的惟一ID

2.5 获取当前线程

public static native Thread currentThread() 用于返回当前线程的引用

2.6 设置线程上下文加载器

getContextClassLoader() 获取线程上下文的类加载器,简单的来讲就是这个线程是有哪一个加载器加载的。

setContextClassLoader() 设置该线程的类加载器,这个方法能够打破java类加载器的父委托机制。

2.7 线程interrupt

以下方法的调用可使当前线程进入阻塞状态,而调用interrupt方法能够打断阻塞。

1)OBject的wait方法

2)OBject的wait(long)方法

3)OBject的wait(long,int)方法

4)Thread的sleep(long)方法

5)Thread的sleep(long,int)方法

6)Thread的join方法

7)Thread的join(long)方法

8)Thread的join(long,int)方法

9)InterruptibleChannel的io操做

10)Selector的wakeup方法

一个线程调用了另外一个被阻塞线程的interrupt方法,则会打断阻塞,打断阻塞并不等于该线程的生命周期结束了,仅仅是打断了当前线程的阻塞状态。

2.8 线程join

某个A线程join,会使当前线程B进入等待状态,直到线程A结束生命周期或者达到给定的时间,此时B处于BLOCKED状态。

2.9 关闭线程

2.9.1 正常关闭

一、线程结束生命周期正常关闭,线程运行结束或者完成了本身的使命就会正常退出

二、捕获中断信号关闭线程,在线程里经过标识来中断线程

三、使用volatile开关控制,使用volatile修饰的开关flag关闭线程也是一种经常使用的方法

2.9.2 异常退出

一个线程的执行单元中是不予许抛出checked异常的,不管是Thread中的run方法仍是Runnable中的run方法,若是运行中过程当中需捕获checked异常而且判断是否还有运行下去的必要,那么此时能够将checked异常封装成unchecked异常抛出进而结束线程的生命周期

2.9.3 进程假死

所谓进程假死就是虽然进程存在,可是没有日志输出,程序不进行任何做业,看起来就想死了同样,但事实上他没有死,程序之因此出现这样的状况,绝大多数的缘由是某个线程阻塞了,或者线程出现了死锁的状况。

 

本文是参考 多线程与高并发编程 一书所作的笔记,都是精简事后的干货,但愿对你们有帮助。后续会继续更新 线程安全与同步、线程池原理、线程间的通讯等相关文章。