02 java多线程基础

1线程简介

线程概念:一个进程包含多个线程,线程是CPU调度和执行的最小单元。在同一个时间点上,一个CPU只能支持一个线程在执行。因为CPU运行的速度很快,因此我们看起来的感觉就像是多线程一样。

1.1 线程的生命周期

 

 

NEW:当我们用关键字new创建一个Thread对象时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态,准确地说,它只是Thread对象的状态,因为在没有start之前,该线程根本不存在,与你用关键字new创建一个普通的java对象没有什么区别。

NEW状态通过start方法进入RUNNABLE状态。

RUNNABLE:线程对象进入RUNNABLE状态必须调用start方法,那么此时才真正地在JVM进程中创建了一个线程,线程一经启动就可以立即执行吗?答案是否定的,线程的运行与否和进程一样都要听令于CPU调度,那么我们把这个中间状态称为可执行状态(RUNNABLE),也就是说他具备执行的资格,但是并没有真正地执行起来而是在等待CPU的调度。严格来讲RUNNABLE的线程只能意外终止或者进入RUNNING状态。

RUNNING:一旦CPU通过轮询或者其它方式从任务可执行队列中选中了线程,那么此时它才能真正地执行自己的逻辑代码,需要说明的一点是一个RUNNING状态的线程事实上也是RUNNABLE的,但是反过来则不成立。在该状态中,线程的状态可以发生如下的状态转换。

  • 直接进入TERMINATED状态,比如调用JDK已经不推荐是用的stop方法。
  • 进入BLOCKED状态,比如调用了sleep,或者wait方法而加入了waitSet
  • 进行某个阻塞的IO操作,比如因网络数据的读写而进入了BLOCKED状态
  • 获取某个锁资源,从而加入到该锁的阻塞队列中而进入了BLOCKED状态。
  • 由于CPU的调度器轮询使该线程放弃执行,进入RUNNABLE状态。
  • 线程主动调用yield方法,放弃cpu执行权,进入RUNNABLE状态。

BLOCKED:该状态中,线程的状态可以发生如下的状态转换

  • 直接进入TERMINATED状态,比如调用JDK不推荐使用的stop方法或者意外死亡(JVM Crash)。
  • 线程阻塞的操作结束,比如读取了想要的数据字节进入到RUNNABLE状态。
  • 线程完成了指定时间的休眠,进入到了RUNNABLE状态。
  • Wait中的线程被其他线程notify/notifyall唤醒,进入RUNNABLE状态。
  • 线程获取到了某个锁资源,进入RUNNABLE状态。
  • 线程在阻塞过程中被打断,比如其他线程调用了interrupt方法,进入RUNNABLE状态。

TERMINATED:TERMINATED状态是一个线程的最终状态,在该状态中线程将不会切换到其他任何状态,线程进入TERMINATED状态,意味着该线程的整个生命周期都结束了,下列情况将会使线程进入TERMINATED状态。

  • 线程运行正常结束,结束生命周期。
  • 线程运行出错意外结束。
  • JVM Crash,导致所有的线程都结束。

 

1.2 守护线程

守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

设置守护线程的方法很简单,调用setDaemon方法即可,true代表守护线程。如果父线程是守护线程,那么子线程也是,反之亦然。

2 Thread API介绍

 

SLEEP:sleep是一个静态方法,参数需要传入毫秒数,会使线程进入指定毫秒数的休眠,暂停执行,时间精度最终要以系统的定时器和调度器的精度为准。sleep有一个重要特性,那就是不会放弃monitor锁的所有权。

YIELD:yield方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前的CPU资源。yield只是一个提示,CPU调度器并不会担保每次都能满足yield提示,如果CPU资源不紧张,则会忽略这种提示

SETPRIORITY:setPriority理论上优先级高的线程会获取到优先被CPU调度的机会,但事实上往往并不会,设置优先级也是一个提示。如果CPU比较忙,设置优先级可能会获得更多的CPU时间片,但是闲时优先级的高低几乎不会有任何作用。

INTERRUPT:当线程处于阻塞状态(调用了sleep、wait、join方法或IO操作阻塞),若另外一个线程调用被阻塞线程的interrupt方法,则会打断这种阻塞,打断阻塞并不等于该线程的生命周期结束,只是打断了当前线程的阻塞状态,会抛出一个InterruptedException的异常。

JOIN:当前线程A join某个线程B,会使当前线程A进入等待,直到线程B结束生命周期,那么在此期间,线程A是处于BLOCKED的。

 

 

3 ThreadGroup

3.1 ThreadGoup与Thread关系

 

 

在java程序中,默认情况下,新的线程都会被加入到main线程所在的group中,main线程的group名字同线程名。如同线程存在父子关系一向,ThreadGroup同样也存在父子关系。下图就很好地说明了父子thread、父子threadGroup以及thread和group之间的层次关系。

 

无论如何,线程都会被加入某个Thread Group中。

3.1 ThreadGoup的基本操作

  • activeGcount()用于获取group中活跃的线程,这只是个估值,并不能百分百地保证数字一定正确,该方法会递归获取其它子group中的活跃线程。
  • activeGroupCount()用于获取group中活跃的子group,这也是一个近似估值,该方法也会递归获取所有的子group。
  • getMaxPriority()用于获取group的优先级,默认情况下,Group的优先级为10,在该group中,所有的线程的优先级不能大于group的优先级。
  • getName()获取group的名字。
  • getParent()获取group的父group,有就返回,没有就返回null。
  • list()该方法没有返回值,执行该方法会将group中所有的活跃的线程信息全部输出到控制台,也就是System.out。
  • parentOf(ThreadGroup g)会判断当前的group的是不是给定group的父group,另外如果给定大group的是给自己本身,那么高返回true。
  • setMaxPriority(int pri)会指定group的最大优先级,最大优先级不能超过父group的最大优先级,该方法会把当前的线程组和子线程组的优先级都有影响。
  • interrupt ()会递归获取子group,然后执行它们各自的interrupt()方法,会将group中所有的active线程都被interrupt

 

3线程的关闭和结束

  • 线程生命周期正常结束。
  • 捕获中断信号关闭线程。
  • 在线程中使用变量开关控制。
  • 线程中发生异常。

 

posted @ 2019-04-21 15:09  java小工匠  阅读(159)  评论(0编辑  收藏  举报