线程不是进程
作为有一定开发经验的程序员来说,在 java 中实现多线程是一件很容易的事情,你只需要将你的类继承 Thread 类或者实现 Runnable 接口就可以。 其实线程完全可以理解为一个任务。 可以同时运行多个任务的程序,就成为多线程程序。
然而线程并非进程。 进程包括线程,每一个进程都拥有一套自己的变量,而线程间则共享这套变量。 从而带来了很多风险,比如最典型的脏数据。这些以后会讨论。
线程状态
在 java 中,线程被定义成有 6 中状态:
NEW
至今尚未启动的线程处于这种状态。 即刚刚 new 出来的 Thread ,但是还未调用 start ()方法。
RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。 该状态是调用了 start() 方法后的状态,出于该状态的线程不一定是正在运行的,他有线程调度器来决定是否运行。
BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。 阻塞与等待不同,阻塞通常是得不到所需要的资源而被迫停下来等待。
WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
TERMINATED
已退出的线程处于这种状态。有两种情况会让线程退出,其一是 run 方法中的任务执行完成,其二是线程执行时出现异常。
java 的线程状态只有如上 6 中,他们以 enum 形式被定义在 Thread.State 中。这里要说一下等待状态,有很多方式能够让线程进入等待状态,比如调用 join ()方法。
join()
对于这个方法,还是要特别的强调一下。在
api
中的解释如下:
Blocks the current Thread (
Thread.currentThread()
) until the receiver finishes its execution and dies.
翻译过来就是:阻塞当前线程,然后让接受者完成任务之后,当前线程才开始继续执行任务。
但是如果接受者在被调用了 join 方法后,有被调用了 interrupt() 方法,则会抛出 java.lang.InterruptedException 异常。可以参考代码 ThreadDemo01 。
- <STRONG> package cn.edu.heut.zcl;
- public class ThreadDemo01 {
- public static void main(String[] args) {
- TA ta = new TA();
- ta.start();
- try {
- ta.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- int i = 0 ;
- while ((i++) < 10 ) {
- System.out.println( "-------" );
- try {
- Thread.sleep( 100 );
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class TA extends Thread {
- @Override
- public void run() {
- super .run();
- int i = 0 ;
- while ((i++) < 40 ) {
- System.out.println( "---->A" );
- if (i == 10 ) {
- Thread.currentThread().interrupt();
- }
- try {
- Thread.sleep( 100 );
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- </STRONG>
package cn.edu.heut.zcl; public class ThreadDemo01 { public static void main(String[] args) { TA ta = new TA(); ta.start(); try { ta.join(); } catch (InterruptedException e) { e.printStackTrace(); } int i = 0; while ((i++) < 10) { System.out.println("-------"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class TA extends Thread { @Override public void run() { super.run(); int i = 0; while ((i++) < 40) { System.out.println("---->A"); if (i == 10) { Thread.currentThread().interrupt(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
记住: join() 方法是将当前线程阻塞
interrupt() 与中断线程
开发时常常需要用到中断线程,在早期的 java 版本中,有 stop() 等方法用来控制线程生死,当时这些方法现在已经废弃,具体愿意以后会谈,同时亦可以参考 sun 的一片文章《 Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated? 》网址如下:
http://download.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
当然, jdk 还是为我们提供了强化终止线程的方法。然而, interrupt 方法只是可以用来请求终止线程。当对一个线程调用 interrupt 方法之后,线程的中断状态将被置位。每一个线程都具有一个 中断状态 ,他是一个 boolean 标志。在实现自己的线程时,应该实时检查该标志,以判断线程是否被中断。
可以通过 Thread.currentThread() 方法得到当前线程,然后通过 is isInterrupted() 方法,来查看中断状态。通常如下实现:
while (!Thread.currentThread().isInterrupted() && 。。。 ) {}
如果你已经运行了 ThreadDemo01 代码,那么你会发现,在 TA 类中如下代码处:
- if (i == 10 ) {
- Thread.currentThread().interrupt();
- }
if (i == 10) { Thread.currentThread().interrupt(); }
将会抛出异常 java.lang.InterruptedException 。这个是由于在 TA 线程中调用了 interrupt 方法后,中断状态已经置为,如果此时再调用 sleep 等阻塞方法后, 该线程不会休眠,想法,他将抛出中断异常并且将中断状态清楚。 所以如下代码是不会让线程 TB 停止。 CopyOfThreadDemo02
- package cn.edu.heut.zcl;
- public class CopyOfThreadDemo02 {
- public static void main(String[] args) {
- TB ta = new TB();
- ta.start();
- int i = 0 ;
- while ((i++) < 10 ) {
- System.out.println( "-------" );
- try {
- Thread.sleep( 100 );
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class TB extends Thread {
- @Override
- public void run() {
- super .run();
- int i = 0 ;
- while (!Thread.currentThread().isInterrupted() && (i++) < 40 ) {
- System.out.println( "---->A" );
- if (i == 4 ) Thread.currentThread().interrupt(); //在sleep之前调用,将不能终止线程
- try {
- Thread.sleep( 100 );
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- //if(i == 4) Thread.currentThread().interrupt();//在sleep之后调用,将能终止线程
- }
- }
- }
package cn.edu.heut.zcl; public class CopyOfThreadDemo02 { public static void main(String[] args) { TB ta = new TB(); ta.start(); int i = 0; while ((i++) < 10) { System.out.println("-------"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class TB extends Thread { @Override public void run() { super.run(); int i = 0; while (!Thread.currentThread().isInterrupted() && (i++) < 40) { System.out.println("---->A"); if(i == 4) Thread.currentThread().interrupt();//在sleep之前调用,将不能终止线程 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //if(i == 4) Thread.currentThread().interrupt();//在sleep之后调用,将能终止线程 } } }
你可以自己试一试,运行如上代码,不能中断 TB 线程。但是如果在 sleep 之后调用 interrupt ,则会中断线程。
interrupted() 与 isInterrupted ()
这两个方法看上去很像,作用也相似可以通过下表来比较两种异同。
|
是否是 static |
返回值 |
不同 |
interrupted |
是 |
当前的中断状态 |
调用后改变中断状态 |
isInterrupted |
否 |
不改变
|
线程属性 ---- 线程优先级
谈优先级可能并不陌生,在 java 中,每一个线程都有一个优先级。默认情况下, 一个线程的优先级直接继承自他的父类。 sun 将 java 的优先级分成 10 级, 1 表示最小优先级, 10 表示最高优先级。同时 jdk 中还定义了 3 个常量 MIN_PRIORITY(1 级 ) 、 MAX_PRIORITY(10 级 ) 、 NORM_PRIORITY(5 级 ) 。
线程的优先级是依赖与平台的, windows 中有 7 个优先级。而在 Linux 中, java 虚拟机将线程的优先级忽略,即所有线程的优先级都一样。 所以在编写程序是,尽量不要依赖于优先级。
setPriority() 方法,顾名思义就是设置优先级的。
yield ()
yield 的中文意思是:屈服。其实理解成让步更加准确。调用该方法,将导致当前执行线程出于让步状态。如果此时有其他线程一起来抢夺 cpu 资源,那么只要这个抢夺的线程的优先级不低于调用线程。则抢夺线程将会被调用。
线程属性 ---- 守护线程
守护线程的用途就是为其他线程提供服务。当被服务者死亡后,其也就没有存在的价值,也就跟着去死了。设置守护线程很简单:
setDaemon ( true )
只需一步,轻松搞定。在使用守护线程时需要记住,永远不要去访问固有资源,如文件、数据库等。以为你不知道什么时候守护线程会结束。
可以参考 DeamonDemo :
- package cn.edu.heut.zcl;
- /**
- * 演示守护线程
- * @author Legend
- *
- */
- public class DeamonDemo {
- public static void main(String[] args){
- DemonRunnable dr = new DemonRunnable();
- dr.setDaemon( true ); //设置为守护线程
- dr.start();
- int i = 0 ;
- while ((i++)< 10 ){
- try {
- Thread.sleep( 500 );
- System.out.println(i);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- private static class DemonRunnable extends Thread{
- @Override
- public void run() {
- super .run();
- while ( true ){
- System.out.println( "------" );
- try {
- Thread.sleep( 100 );
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }