线程基础-----详细介绍线程状态转换(一)

1、场景:

接触到java,若是要继续深刻学习,那么就必需要了解线程,这个过程是比较长的。若是在实际工做中没有接触到,本身摸索的话,始终不能得其道,固然这并不妨碍,咱们将线程基础学习一翻。之后有了实际的需求驱动,那么就会事半功倍。java

在学习的时候,线程的状态转换,线程调度,同步异步,线程的通讯这些基本的知识固然要紧紧掌握。在摸索中前进。编程

在这里为你们推荐一个网站 并发编程网多线程

2、线程基础

 一、线程状态转换

图示说明:并发

一、新建状态:新建了一个线程对象。异步

二、就绪状态(Runnable):线程对象建立后,其余线程对象调用了该线程对象的start()方法,该状态的线程位于可运行的线程池中,变得能够运行,等待得到cpu使用权。jvm

 三、运行状态(running):就绪状态的线程得到cpu,开始执行该线程的代码。学习

四、阻塞状态(Blocked):阻塞状态是由于,线程因为某种缘由放弃cpu使用权,暂时中止运行。知道线程进入就绪状态,才有机会转到运行状态。阻塞状态状况分为三种:(该状态为中止当前线程,当时并不释放其所占用的资源)网站

 4.1等待阻塞:运行的线程执行了wait,jvm会把该线程放入等待的池中。spa

4.2同步阻塞:运行的线程在获取对象同步锁时,若该同步锁被别人的线程占用,则该jvm会把线程放入锁定池中。.net

4.3其余阻塞:运行的线程执行执行sleep()或者join()方法,或者发出io请求时,jvm会将该线程设置为阻塞。当sleep()状态超时,join等待线程终止或超时,或者io处理完毕,线程从新转为就绪状态。

五、死亡状态(dead):线程执行完了,或者因异常退出了run方法,该线程生命周期结束。

关于java线程调度的几个方法的解释说明:

(一)当线程调用了自身的sleep()或者其余线程的join()方法时,就会进入阻塞状态,当sleep()或者join方法结束后,该线程转为可运行状态,继续等待os分配cpu时间片

(二)线程调用了yield方法,意思是放弃当前的cpu时间分片,回到可运行状态,这时与其余可运行状态,这时与其余进程处于同等竞争状态,os有可能会接着让这个线程进入运行状态;(注:yield并非让线程进入就绪状态,而是运行状态,可是不占有cpu时间片而已)

(三)当线程刚进入可运行状态(即就绪状态),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会当即进入锁池状态,等待获取锁标记(这时的锁定池里也许已经有了其余线程在等待获取锁标记,这时他们处于队列状态,即先到先得),一旦线程得到线程锁标记后,就可转入可运行状态,等待os分配cpu时间片;

(四)当线程调用wait()方法后进入等待队列(进入到这个状态会释放所占有的资源,与阻塞不一样),这个状态是不可以被自动唤醒的,必须依赖于其余线程调用notify()或者notifyall()方法才唤醒(wait(1000)能够自动唤醒)(因为notify()只是唤醒一个线程,但咱们不能肯定具体唤醒的是那个线程,也许咱们须要唤醒的线程不可以被唤醒,所以在实际使用时,通常都使用notifyall()方法,唤醒全部的线程),线程被唤醒后会进入锁池,等待获取锁标记

 (五)wait()和notify()方法:当一个线程执行到wait()方法时,他就进入到一个和对象相关的等待池中,同时失去了对象锁。当他被一个notify()方法唤醒的时,等待池中的线程就被放到了锁定池中。该线程从池中获的锁,而后回到wait()方法前的中断现场。

调用sleep,join时,不会释放所占用的资源,因此会进入到阻塞状态。

 调用wait时,会释放所占用的资源,因此会进入到等待队列。

更加细致的图示:


注:几个线程方法的介绍:

一、wait()

public final void wait()throws InterruptedException,IllegalMonitorStateException

该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用wait()以前,线程必需要得到该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。进入wait()方法后,当前线程释放锁。在从wait()返回前,线程与其余线程竞争从新得到锁。若是调用wait()时,没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,所以,不须要try-catch结构。

二、notify()

 public final native void notify() throws IllegalMonitorStateException

该方法也要在同步方法或同步块中调用,即在调用前,线程也必需要得到该对象的对象级别锁,的若是调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。

该方法用来通知那些可能等待该对象的对象锁的其余线程。若是有多个线程等待,则线程规划器任意挑选出其中一个wait()状态的线程来发出通知,并使它等待获取该对象的对象锁(notify后,当前线程不会立刻释放该对象锁,wait所在的线程并不能立刻获取该对象锁,要等到程序退出synchronized代码块后,当前线程才会释放锁,wait所在的线程也才能够获取该对象锁),但不惊动其余一样在等待被该对象notify的线程们。当第一个得到了该对象锁的wait线程运行完毕之后,它会释放掉该对象锁,此时若是该对象没有再次使用notify语句,则即使该对象已经空闲,其余wait状态等待的线程因为没有获得该对象的通知,会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。这里须要注意:它们等待的是被notify或notifyAll,而不是锁。这与下面的notifyAll()方法执行后的状况不一样。

三、notifyAll()

 public final native void notifyAll() throws IllegalMonitorStateException

该方法与notify()方法的工做方式相同,重要的一点差别是:

notifyAll使全部原来在该对象上wait的线程通通退出wait的状态(即所有被唤醒,再也不等待notify或notifyAll,但因为此时尚未获取到该对象锁,所以还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll线程退出调用了notifyAll的synchronized代码块的时候),他们就会去竞争。若是其中一个线程得到了该对象锁,它就会继续往下执行,在它退出synchronized代码块,释放锁后,其余的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到全部被唤醒的线程都执行完毕。

四、wait(long)和wait(long,int)

 显然,这两个方法是设置等待超时时间的,后者在超值时间上加上ns,精度也难以达到,所以,该方法不多使用。对于前者,若是在等待线程接到通知或被中断以前,已经超过了指定的毫秒数,则它经过竞争从新得到锁,并从wait(long)返回。另外,须要知道,若是设置了超时时间,当wait()返回时,咱们不能肯定它是由于接到了通知仍是由于超时而返回的,由于wait()方法不会返回任何相关的信息。但通常能够经过设置标志位来判断,在notify以前改变标志位的值,在wait()方法后读取该标志位的值来判断,固然为了保证notify不被遗漏,咱们还须要另一个标志位来循环判断是否调用wait()方法。

深刻理解:

若是线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

 当有线程调用了对象的notifyAll()方法(唤醒全部wait线程)或notify()方法(只随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

 优先级高的线程竞争到对象锁的几率大,倘若某线程没有竞争到该对象锁,它还会留在锁池中,惟有线程再次调用wait()方法,它才会从新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

几个线程方法介绍转载于“http://blog.csdn.net/ns_code/article/details/17225469” 已经写得够清楚了,不必我再赘述一次,再根据我文章里面画的线程状态转换图,会很清晰的理解线程状态转换。

注:如下图是我照着网友的图画的。

下面的图为运行状态到锁池状态的转换



注:借鉴网上资料进行整理,再加上本身的理解,将图从新画了一遍。目的是可以更加清晰的理解线程状态转换,线程状态转换是写好多线程的代码的基础。

再贴其余相似的文章,http://my.oschina.net/zhdkn/blog/114301http://my.oschina.net/mingdongcheng/blog/139263

相关文章
相关标签/搜索