Java并发之线程状态及Thread常用方法

本篇文章主要讲解线程的虚拟机状态和线程基本方法,希望可以加深对线程的使用理解。

一、线程的虚拟机状态

线程对象在不同的运行期间有不同的状态,状态信息定义在Thread公共静态枚举java.lang.Thread.State中,线程可以处于以下6种状态,一个线程在给定的时间点只能处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态

NEW
新创建的线程在尚未启动时处于此状态。即new Thread()一个线程在未调用start()方法时处于此状态。

RUNNABLE
在Java虚拟机中执行的线程处于这种状态。线程调用start()方法后,线程进入可运行线程池(就绪状态),接受CPU调度执行。该状态是线程处于正常运行中,
这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等。

BLOCKED
阻塞等待监视器锁定的线程处于此状态。这个状态一般发生在多线程有同步操作的情况下,比如一个线程正在等待一个线程synchronized 块的执行释放,
或者可重入的 synchronized块里别人调用wait()方法, 也就是这里是线程在等待进入临界区

WAITING
无限期地等待另一个线程执行特定动作的线程处于这种状态。
1)这个状态是当线程获得了对象锁之后,调用了wait()方法,等待该对象锁的拥有者调用notify()或notifyAll()唤醒。
2)当在主线程里,调用子线程的join()后,主线程也会处于waiting状态,等待join的子线程执行完成后,继续执行。

TIMED_WAITING
正在等待另一个线程执行动作达到指定的等待时间的线程处于此状态。一般出现在一个线程在其它线程调用sleep(long)或wait(long),join(long)后等待的情况。

TERMINATED
退出的线程处于这种状态。比如run方法正常执行完退出,线程调用stop()方法强制停止退出,线程运行时出现异常中断退出等。

BLOCKED 和 WATING 的区别

BLOCKED是线程在同步锁阻塞后进入Lock Pool等待重新获取锁执行, WATING是线程在获得同步锁后进入Waiting Pool等待别人notify唤醒继续执行。一个在临界点外部等待进入,一个在临界点里面等待唤醒。

调用与线程相关的方法是造成线程状态改变的主要原因,在调用与线程有关的方法后,线程进入不同的状态,这些状态之间有些是可以相互切换的,但有些的只能是单向切换的。

二、线程Thread常用方法

对使用Java并发编程的,一定会涉及到多线程,说到线程就离不开Thread这个类,我们先不管Thread实现原理是啥,从实际使用的角度出发,我们总结一下线程的常用方法都有哪些,以及怎么使用,会遇到哪些问题。

1.start()方法

start方法是为了启动线程运行。当线程创建后,调用了start(),此时线程处于就绪状态,等待CPU调度准备执行。

2.run()方法

run()方法里是线程具体的代码执行逻辑,不管是继承Thread类还是实现Runnable,都会覆写run方法。当调用了线程的start(),CPU有空闲时便会执行run方法。

是否可以直接调用run()方法?可以。但是,直接调用就相当于对一个普通对象的普通方法调用,也就没有了线程异步调度,一般不会直接调用。

3.sleep方法

sleep()是让当前正在执行的线程在指定的毫秒数内休眠(暂停执行),这个当前线程即Thread.currentThread()返回的线程。

线程的sleep()不会释放当前线程持有的锁,而是让出CPU资源给其他线程。

4.currentThread()方法

Thread.currentThread()返回的是执行的代码片段正在被那个线程调用的信息。如Thread.currentThread().getId()得到是被调用线程的唯一标识ID;Thread.currentThread().getName()得到是被调用线程的线程名称。

注意currentThread()与this的使用区别。

5.wait()

该方法是Object类的方法,用来将线程置入“等待执行队列”中。

在调用wait()方法前,只能在同步方法或同步代码块中调用wait()方法,即线程必须获得对象级别锁,否则会抛出IllegalMonitorStateException异常(RunntimeException的子类)。

wait()方法作用是使当前执行代码的线程进行等待,执行wait()后,在wait()所在的代码行处线程将停止继续执行,当前线程的对象锁释放,直到被通知唤醒或中断为止。 在继续执行wait()后面代码之前,该线程需要重新获得对象锁才可以继续执行。

6.notify()方法

该方法是Object类的方法。

notify()方法也需要在同步方法或同步块中调用,即在调用前,线程必须获得对象级别锁,否则也会抛出IllegalMonitorStateException异常。

该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程在等待,则由线程规划器挑选其中一个呈wait()状态的线程,对其通知唤醒,使它重新获取该对象的对象锁,继续执行后续代码。

执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,而是要等到该执行notify()的线程把程序执行完。也就是退出synchronized()代码块后,当前线程才会释放锁,此时去通知呈wait状态的线程去重新获取该对象锁。

notify()是用来唤醒持有该对象锁的等待池中的某一个线程;notifyAll()是用来唤醒持有该对象锁的等待池中的所有线程。

7.yield()方法

yield()方法是让出当前的CPU资源,将它给其它的任务去占用CPU执行。

暂停当前正在执行的的线程,去执行其他线程。让当前正在执行的线程处于可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

8.join()方法

join()方法的作用是当使所属的子线程对象正在执行run()方法中的任务时,而使当前线程进行无限期的阻塞,直到子线程销毁后再继续执行当前线程后续的代码。其作用就是等待线程对象销毁。

很多情况下,主线程创建,并启动了子线程,如果子线程中要进行大量的长时间的运行,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完后再结束,就要在主线程中调用子线程对象的join()方法了。

9.interrupt()方法

用来中断线程运行,但并不是像在循环里break那样,马上停止循环。

线程调用interrupt()方法仅仅是在调用的线程里设置了停止的标记,并不是立即停止线程。标记为是记在this(线程对象)里。

10.interrupted()方法、isInterrupted()方法

interrupted()返回当前线程是否已经中断的标记值,并清除中断标记位。注意是当前线程currentThread()的标记,源码片段如下,

 public static boolean interrupted() {  return currentThread().isInterrupted(true);  } 

isInterrupted()返回是调用该方法的对象的线程的中断标记值。源码片段如下,

 public boolean isInterrupted() {  return isInterrupted(false); }

两者区别:

interrupted()是静态方法(它测试的是当前线程的中断状态),并且有清除标记功能,isInterrupted()是实例方法(它测试的是实例对象所表示的线程的中断状态)。

开发人员一般会根据中断标记的值来决定是否中断线程的运行,常用的是判断中断标记值手动抛出异常的方式来达到停止线程运行的目的。

posted @ 2017-12-22 16:20  garryfu  阅读(530)  评论(0编辑  收藏  举报