Java中的多线程(转)

多线程的简单实现过程:

(1)将任务处理代码移到实现了Runnable接口的类的run方法中。这个接口非常简单,只有一个方法:

public interface Runnable

{

void run();

}

实现这样一个类:

class MyRunnable implements Runnable

{

public void run()

{

 

}

}

创建一个这个类的对象:

Runnable r=new MyRunnable();

再由Runnable创建一个Thread对象:

Thread t=new Thread(r);

启动线程:

t.start();

(2)还有一种在代码书写上比较方便的方法,创建Thread类的子类。

class MyThread extends Thread

{

public void run()

{

 

}

}

创建这个类的一个对象,然后调用start方法。

注意,如果线程很少,可以使用以上方法,如果线程太多,建议使用线程池。

2009.5.23

线程的中断:

Java中线程是不能强制中断的,相反的,每一个线程都有一个中断状态标志位。当外部代码想终止某个线程时,可以调用这个线程的interrupt方法。调用后,此线程的中断状态标志位自动设置为真(isInterrupted()==true),如果此线程中有对中断状态标志位的判断,则可以决定是否响应中断请求。

以上是什么意思呢?就是说,线程会判断是否有人想中断自己,如果有人想中断,则自己可以决定是不是真的中断,如果这个线程非常重要,则完全可以不理会中断请求而继续执行。

所以,典型的线程代码如下:

while(!Thread.currentThread().isInterrupted())

{

 

}

当然,这个代码还不是全部的,因为,还有另一种情况没有处理,那就是,如果当前线程正在sleep或者wait怎么办呢?

当调用某线程的interrupt方法而此线程正在休眠时,将在此线程内部产生InterruptedException异常,以中断线程的sleep或者wait。其实我们发现在sleep时,Java强制要求加try,就是为了捕获这个异常。一个典型的sleep代码如下:

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

此时代码正在Thread.sleep(1000)这一行阻塞着呢,突然被interrupt,就跳到catch里了。

所以,一个线程的典型代码结构有两个,第一个是不经常sleep的:

public void run()

{

try

{

......

while(!Thread.currentThread().isInterrupted())

{

 

}

}

catch(InterruptedException e)

{

//在sleep或者wait的时候被中断,就会跳到这里

}

finally

{

//清理现场

}

}

以上代码,由于不经常使用sleep,所以要定期检查中断状态,来处理中断情况。

如果每一次循环都需要sleep的话,那这个isInterrupted检查就不是必需的了。因为,当代码sleep或wait时,被中断后将直接进入异常处理,如果不在sleep或者wait,那么当调用sleep或wait的时候,sleep或wait会检测到中断状态标志位为真而直接抛出异常。代码如下:

public void run()

{

try

{

......

while(expression)

{

 

Thread.sleep(delay);

}

}

catch(InterruptedException e)

{

//被中断了

}

finally

{

//清理现场

}

}

注意:当sleep抛出异常的同时会清除中断状态。

注意:有两个非常类似的方法,interrupted和isInterrupted。interrupted是一个静态方法,它检查当前线程是否已被中断。而且调用interrupted方法会清除该线程的中断状态。另一方面,isInterrupted方法是一个实例方法,可以用它来检查是否线程被中断了。调用它不改变中断状态值。

如果A线程想要等待B线程执行完毕后再执行,则可以这样:

B.join();

调用B的阻塞方法,将A阻塞掉,直到B返回。

在很多发布的代码中,我们经常看到InterruptedException异常被抑制在了一个很低的层次上,像这样:

void mySubTask()

{

......

try{sleep(delay);}

catch(InterruptedException e){}//这里不应该忽略处理代码

......

}

忽略错误处理是非常不可取的,如果不改动这个结构,则有两个比较合理的选择:

第一,在catch中加入Thread.currentThread().interrupt()设置中断状态,这样可以保证不破坏中断状态,这样调用者可以对其进行测试。

第二,更好的选择是,标记出我们的方法将抛出InterruptedException异常,不采用try语句块捕获异常。那么,调用者(或者,最终的run方法)就能够捕获该异常。

void mySubTask() throws InterruptedException

{

......

sleep(delay);

......

}

线程的状态:

线程有四个状态:

New(新生):new出来还没有运行的线程,在运行之前还有一些工作要做。

Runnable(可运行):这里包括两种状态,一种是正在运行,一种是有机会运行,但时间片还没有得到。

Blocked(被阻塞):线程不能动了。

Dead(死亡):有自然死亡(run执行完毕)和猝死(run出现异常而未被处理)两类。

注意:无法确定一个活着的线程是可运行的还是被阻塞了,也无法确定一个可运行的线程是否正在运行。另外,还无法区分死亡线程和非可运行线程。

线程的同步:

Java中的典型同步方式有两种:块同步和方法同步,两种方式都用到了synchronized关键字。

同步方法:

如果对一个类中的方法添加了synchronized关键字,它就成了同步方法,当一个线程调用这个同步方法的时候,另一个线程调用这个类的所有同步方法时都会阻塞,直到前一个线程调用完毕。

典型代码如下:

public synchronized void method()

{

 

}

同步块:

和VS一样,对某个引用类型对象上锁:

synchronized(s)

{

......

s.add();

......

}

wait和notify是object方法,而不是Thread的方法。wait可以让当前获得锁的线程暂时放弃,让其它正在阻塞的线程有机会执行。如果有wait,在代码的末尾一定要加notify或notifyAll,将放弃锁的线程惊醒。

wait一般用在两个或多个线程协作上。比如A、B两线程共同操作C,A线程处理C已经很长时间了,B却还没开始。如果需要AB两者执行差距不太大,则A可以先wait,让B有机会处理C。

posted @ 2012-08-06 14:20  petercao  阅读(1595)  评论(0编辑  收藏  举报