今天编写多线程遇到了个问题,所以上网找了些java多线程的资料,现做一下总结。
-----------------------------------------------------------------------------------------
?
先来说一下我的问题:
父线程开启一个子线程,子线程中Thread.sleep(timeout),如果父线程没有退出,子线程可以从睡眠中醒来,但是如果父线程已经退出,子线程就不会醒来,就是说Thread.sleep(timeout)后面的代码将不会执行。
?
------------------------------------------------------------------------------------------
这个哥们写得很好理解,转一下:http://express.ruanko.com/ruanko-express_6/webpage/tech4.html
?
多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。
首先我们先来认识一下进程、线程、并发执行的概念:
一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。
在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。
下面我以一个日常生活中简单的例子来说明进程和线程之间的区别和联系:
这副图是一个双向多车道的道路图,假如我们把整条道路看成是一个“进程”的话,那么图中由白色虚线分隔开来的各个车道就是进程中的各个“线程”了。
Java编写的程序都运行在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。
每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行的。JVM找到程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后,主线程运行完成。JVM进程也随即退出。
操作系统将进程线程进行管理,轮流(没有固定的顺序)分配每个进程很短的一段时间(不一定是均分),然后在每个进程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。
Java语言对多线程的支持通过类Thread和接口Runnable来实现。这里就不多说了。这里重点强调两个地方:
// 主线程其它代码段
ThreadClass?subThread?=?new?ThreadClass();
subThread.start();
// 主线程其它代码段
subThread.sleep(1000);
有人认为以下的代码在调用start()方法后,肯定是先启动子线程,然后主线程继续执行。在调用sleep()方法后CPU什么都不做,就在那里等待休眠的时间结束。实际上这种理解是错误的。因为:
前面我们提到,由于线程何时执行是未知的,只有在CPU为线程分配到时间片时,线程才能真正执行。在线程执行的过程中,由可能会因为各种各样的原因而暂停(就像前面所举的例子一样:汽车只有在交通灯变绿的时候才能够通行,而且在行驶的过程中可能会出现塞车,等待其它车辆通行或转弯的状况)。
这样线程就有了“状态”的概念,下面这副图很好的反映了线程在不同情况下的状态变化。
Java中关于线程调度的API最主要的有下面几个:
关于这几个方法的详细应用,可以参考SUN的API。这里我重点总结一下这几个方法的区别和使用。
sleep方法与wait方法的区别:
sleep/wait与yeld方法的区别:调用sleep或wait方法后,线程即进入block状态,而调用yeld方法后,线程进入runnable状态。
wait与join方法的区别:
通过上面的介绍相信同学们对java里面的多线程已经有了基本的了解和认识。其实多线程编程并没有大家想象中的那么难,只要在实际的学习,工作当中不断的加以练习和使用,相信大家很快就能掌握其中的奥妙,从而编写出赏心悦目的java程序。
?
------------------------------------------------------------------------------------------
?
Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别?
从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列)。并且在某一时刻cpu只为ready queue中位于队列头部的线程服务。?
但是当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,这就是yield。?
或者当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep。?
sleep方法不推荐使用,可用wait。?
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。?
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。?
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。?
当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。?
waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。?
线程的状态?
线程有四种状态,任何一个线程肯定处于这四种状态中的一种:?
1)??? 产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没对它调用start()函数之前。?
2)??? 可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表示它可能正处于线程池中等待排排程器启动它;也可能它已正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态,但显而易见的是此时线程不一定正在执行中。?
3)??? 死亡(Dead):当一个线程正常结束,它便处于死亡状态。如一个线程的run()函数执行完毕后线程就进入死亡状态。?
4)??? 停滞(Blocked):当一个线程处于停滞状态时,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态时,它有可能重新执行。如通过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态。?
用法?
sleep()?
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。?
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。?
wait()和notify()和notifyAll()?
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。?
wait可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用,我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!?
好,那怎么把对象锁收回来呢??
第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.?
第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.?
那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了.?
notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。?
notifyAll()唤醒在此对象监视器上等待的所有线程。?
suspend和resume()?
join()?
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。?
yield()?
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。?
interrupt()?
interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。?
各个方法之间的区别
------------------------------------------------------------------------------------------