因为种种需求,今天开始由浅入深的学习java的多线程机制,而java语言的一大特性点就是内置对多线程的支持。java
如下内容出自: http://blog.csdn.net/jiangwei0910410003/article/details/19962627 : 缓存
1.新建状态(New):用new语句建立的线程对象处于新建状态,此时它和其它的java对象同样,仅仅在堆中被分配了内存
2.就绪状态(Runnable):当一个线程建立了之后,其余的线程调用了它的start()方法,该线程就进入了就绪状态。处于这个状态的 线程位于可运行池中,等待得到CPU的使用权
3.运行状态(Running): 处于这个状态的线程占用CPU,执行程序的代码
4.阻塞状态(Blocked): 当线程处于阻塞状态时,java虚拟机不会给线程分配CPU,直到线程从新进入就绪状态,它才有机会转到 运行状态。
阻塞状态分为三种状况:
1)、 位于对象等待池中的阻塞状态:当线程运行时,若是执行了某个对象的wait()方法,java虚拟机就回把线程放到这个对象的等待池中
2)、 位于对象锁中的阻塞状态,当线程处于运行状态时,试图得到某个对象的同步锁时,若是该对象的同步锁已经被其余的线程占用,JVM就会把这个线程放到这个对象的琐池中。
安全
3)、 其它的阻塞状态:当前线程执行了sleep()方法,或者调用了其它线程的join()方法,或者发出了I/O请求时,就会进入这个状态中。多线程
当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。能够经过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于中止状态。app
一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程当中,能够经过两个方法使线程暂时中止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后,能够经过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不必定会立刻执行,只是进入了就绪状态,等待着系统进行调度)。suspend方法是不释放锁
异步
虽然suspend和resume能够很方便地使线程挂起和唤醒,但因为使用这两个方法可能会形成一些不可预料的事情发生,所以,这两个方法被标识为deprecated(弃用)标记,这代表在之后的jdk版本中这两个方法可能被删除,因此尽可能不要使用这两个方法来操做线程。函数
有三种方法可使终止线程。
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2. 使用stop方法强行终止线程(线程中调用了阻塞代码)(这个方法不推荐使用,由于stop是依靠抛出异常来结束线程的,也可能发生不可预料的结果)。若是没有调用阻塞代码,能够正常结束线程。
学习
3. 使用interrupt方法中断线程(线程中调用了阻塞代码)(其实这种方法也是经过抛出异常来结束线程的)。若是没有调用阻塞代码,能够经过判断线程的中断标志位来介绍线程。this
join():等待此线程死亡后再继续,可以使异步线程变为同步线程,join方法是不会释放锁
interrupt():中断线程,被中断线程会抛InterruptedException
wait():等待获取锁:表示等待获取某个锁执行了该方法的线程释放对象的锁,JVM会把该线程放到对象的等待池中。该线程等待其它线程唤醒
notify():执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。使线程由阻塞队列进入就绪状态
sleep():让当前正在执行的线程休眠,有一个用法能够代替yield函数——sleep(0)
yield():暂停当前正在执行的线程对象,并执行其余线程。也就是交出CPU一段时间(其余一样的优先级或者更高优先级的线程能够获取到运行的机会)
sleep和yield区别:
一、sleep()方法会给其余线程运行的机会,而不考虑其余线程的优先级,所以会给较低线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
二、当线程执行了sleep(long millis)方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法后,将转到就绪状态。
三、sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常
spa
四、sleep()方法比yield()方法具备更好的移植性
若是但愿明确地让一个线程给另一个线程运行的机会,能够采起如下的办法之一:
一、调整各个线程的优先级
二、让处于运行状态的线程调用Thread.sleep()方法
三、让处于运行状态的线程调用Thread.yield()方法
四、让处于运行状态的线程调用另外一个线程的join()方法
首先,wait()和notify(),notifyAll()是Object类的方法,sleep()和yield()是Thread类的方法。
(1).经常使用的wait方法有wait()和wait(long timeout):
void wait() 在其余线程调用此对象的 notify() 方法或 notifyAll() 方法前,致使当前线程等待。
void wait(long timeout) 在其余线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,致使当前线程等待。
wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
wait()和notify()由于会对对象的“锁标志”进行操做,因此它们必须在synchronized函数或synchronized代码块中进行调用。若是在non- synchronized函数或non-synchronized代码块中进行调用,虽然能编译经过,但在运 行时会发生IllegalMonitorStateException的异常。
(2).Thread.sleep(long millis),必须带有一个时间参数。
sleep(long)使当前线程进入停滞状态,因此执行sleep()的线程在指定的时间内确定不会被执行;
sleep(long)可以使优先级低的线程获得执行的机会,固然也可让同优先级和高优先级的线程有执行的机会;
sleep(long)是不会释放锁标志的。
(3).yield()没有参数。
sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出CPU占有权,但让出的时间是不可设定的。yield()也不会释放锁标志。
实际上,yield()方法对应了以下操做: 先检测当前是否有相同优先级的线程处于同可运行状态,若有,则把 CPU 的占有权交给此线程,不然继续运行原来的线程。因此yield()方法称为“退让”,它把运行机会让给了同等优先级的其余线程。
sleep方法容许较低优先级的线程得到运行机会,但yield()方法执行时,当前线程仍处在可运行状态,因此不可能让出较低优先级的线程些时得到CPU占有权。 在一个运行系统中,若是较高优先级的线程没有调用 sleep 方法,又没有受到 I/O阻塞,那么较低优先级线程只能等待全部较高优先级的线程运行结束,才有机会运行。
yield()只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行。因此yield()只能使同优先级的线程有执行的机会。
volatile至关于synchronized的弱实现,也就是说volatile实现了相似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其余的线程。
volatile包含如下语义:
(1)Java 存储模型不会对valatile指令的操做进行重排序:这个保证对volatile变量的操做时按照指令的出现顺序执行的。
(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其余对CPU不可见的地方,每次老是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程老是可见的,而且不是使用本身线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操做后,其后的任何读操做理解可见此写操做的结果。
尽管volatile变量的特性不错,可是volatile并不能保证线程安全的,也就是说volatile字段的操做不是原子性的,volatile变量只能保证可见性(一个线程修改后其它线程可以理解看到此变化后的结果),要想保证原子性,目前为止只能加锁!
线程同步的特征:
一、若是一个同步代码块和非同步代码块同时操做共享资源,仍然会形成对共享资源的竞争。由于当一个线程执行一个对象的同步代码块时,其余的线程仍然能够执行对象的非同步代码块。(所谓的线程之间保持同步,是指不一样的线程在执行同一个对象的同步代码块时,由于要得到对象的同步锁而互相牵制)
二、每一个对象都有惟一的同步锁
三、在静态方法前面可使用synchronized修饰符,可是要注意的是锁对象是类(用Object.class而不能用this),而不是这个类的对象。
四、当一个线程开始执行同步代码块时,并不意味着必须以不间断的方式运行,进入同步代码块的线程能够执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其余的线程。
五、synchronized声明不会被继承,若是一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不在保持同步,除非用synchronized修饰。
六、synchronized 关键字可以修饰一个对象实例中的函数或者代码块。 在一个非静态方法中 this 关键字表示当前的实例对象。 在一个 synchronized 修饰的静态的方法中,这个方法所在的类使用 Class 做为实例对象。
再稍作点补充:
关于中断的四种缘由:
1) JVM将cpu资源从当前线程切换给其余线程,使本线程让出cpu的使用权处于中断转态。
2) 线程使用cpu资源期间,执行了sleep( int millsecoond ) 方法, 使当前线程进入休眠状态,该方法是Thread的一个类方法,线程一旦执行了此方法,就马上让出cpu的使用权,使当前线程处于中断状态。通过参数指定的时间后,该线程就从新进到线程队列中排队等待cpu资源,以便从中断处继续运行。
3) 执行了wait()方法,是的当前线程进入等待状态,等待状态的前后才能不会主动进到线程队列中排队等待cpu资源,必须有其它线程调用notify()方法通知它,使得它从新进到线程队列中排队等待cpu资源。
4) 执行了某个操做进入阻塞西黄太,好比读写操做。进入阻塞状态时线程不能进入排队队列,只有当引发阻塞的缘由消除时,线程才从新进到线程队列中排队等待cpu资源。
死亡:
处于死亡状态的线程不具备继续运行的能力,线程死亡的缘由有二:
1) 执行完了run()中的所有语句,线程完成了它的所有工做。结束了run()方法。
2) 线程被提早强制性的终止,即强制run()方法结束。
所谓死亡状态就是线程释放了实体,即释放分配给线程对象的内存。