读书人

Java Thread的1点知识(II)

发布时间: 2012-10-07 17:28:51 作者: rapoo

Java Thread的一点知识(II)

?

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说“停止线程”了。而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级,(b)正在运行的线程因为其它原因而阻塞。

wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,释放当前线程锁定的任何对象。进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

sleep()方法是本地方法,属于Thread类,它有两种定义:

public static native void sleep(long millis) throws InterruptedException;??

public static void sleep(long millis, int nanos) throws InterruptedException {?

??? //other code?

}?

其中的参数millis代表毫秒数(千分之一秒),nanos代表纳秒数(十亿分之一秒)。这两个方法都可以让调用它的线程沉睡(停止运行)指定的时间,到了这个时间,线程就会自动醒来,变为可运行状态(RUNNABLE),但这并不表示它马上就会被运行,因为线程调度机制恢复线程的运行也需要时间。调用sleep()方法并不会让线程释放它所持有的同步锁;而且在这期间它也不会阻碍其它线程的运行。上面的2个方法都声明抛出一个 InterruptedException类型的异常,这是因为线程在sleep()期间,有可能被持有它的引用的其它线程调用它的 interrupt()方法而中断。中断一个线程会导致一个InterruptedException异常的产生,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。

为了更好地理解interrupt()效果,我们来看一下下面这个例子:

public class InterruptTest {?

??? public static void main(String[] args) {?

??????? Thread t = new Thread() {?

??????????? public void run() {?

??????????????? try {?

??????????????????? System.out.println("我被执行了-在sleep()方法前");?

????????????? ??????// 停止运行10分钟?

??????????????????? Thread.sleep(1000 * 60 * 60 * 10);?

??????????????????? System.out.println("我被执行了-在sleep()方法后");?

??????????????? } catch (InterruptedException e) {?

??????????????????? System.out.println("我被执行了-在catch语句块中");?

??????????????? }?

??????????????? System.out.println("我被执行了-在try{}语句块后");?

??????????? }?

??????? };?

??????? // 启动线程?

??????? t.start();?

??????? // 在sleep()结束前中断它?

????? ??t.interrupt();?

??? }?

}?

运行结果:

我被执行了-在sleep()方法前

我被执行了-在catch语句块中

我被执行了-在try{}语句块后

?

wait()方法也是本地方法,属于Object类,有三个定义:

public final void wait() throws InterruptedException {?

??? //do something?

}??

?

?

1) sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

2) sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。

public class ThreadTest implements Runnable{

public void run(){

for(int k=0;k<10;k++){

if(k == 5 && Thread.currentThread().getName().equals("t1")){

Thread.yield();

}

System.out.println(Thread.currentThread().getName()+ " : " + k);

}

}

public static void main(String[] args) {

Runnable r = new ThreadTest();

Thread t1 = new Thread(r,"t1");

Thread t2 = new Thread(r,"t2");

t1.setPriority(Thread.MAX_PRIORITY);

t2.setPriority(Thread.MIN_PRIORITY);

t1.start();

t2.start();

}

}

输出结果:

t1 : 0

t1 : 1

t1 : 2

t1 : 3

t1 : 4

t1 : 5

t1 : 6

t1 : 7

t1 : 8

t1 : 9

t2 : 0

t2 : 1

t2 : 2

t2 : 3

t2 : 4

t2 : 5

t2 : 6

t2 : 7

t2 : 8

t2 : 9

多次运行这个程序,输出也是一样。这说明:yield()方法不会使不同优先级的线程有执行的机会。

?

?

isInterrupted()和interrputed()方法的区别

isInterrupted方法是实例方法,interrupted方法是静态方法。

Thread.currentThread().isInterrupted()

Thread.interrupted()

首先说明:wait(),notify(),notifyAll()这些方法由java.lang.Object 类提供,而上面讲到的方法都是由java.lang.Thread 类提供(Thread类实现了Runnable 接口)。wait(),notify(),notifyAll()这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized 语句块内使用这三个方法。先看下面了例子:

示例12:

public class ThreadTest implements Runnable{

public static int shareVar = 0;

public synchronized void run(){

if(shareVar == 0){

for(int i=0;i<10;i++){

shareVar++ ;

if(shareVar == 5){

try{

this.wait();

}catch(Exception e){}

}} }

if(shareVar != 0){

System.out.print(Thread.currentThread().getName());

System.out.println(" shareVar = " + shareVar);

this.notify();

}

}

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r,"t1");

Thread t2 = new Thread(r,"t2");

t1.start();

t2.start();

}}

运行结果:

t2 shareVar = 5

t1 shareVar = 10

t1线程最先执行。由于初始状态下shareVar 为0,t1 将使shareVar 连续加1,当shareVar的值为5 时,t1 调用wait()方法,t1 将处于休息状态,同时释放锁标志。这时t2 得到了锁标志开始执行,shareVar 的值已经变为5,所以t2 直接输出shareVar 的值,然后再调用notify() 方法唤醒t1。t1 接着上次休息前的进度继续执行,把shareVar 的值一直加到10,由于此刻shareVar 的值不为0,所以t1 将输出此刻shareVar的值,然后再调用notify()方法,由于此刻已经没有等待锁标志的线程,所以此调用语句不起任何作用。

?

?

?

?

wait()->本线程进入等待队列,只能通过别的线程的notify()或notifyall()唤醒??

? notify()->随机地从等待队列删除一个线程,也就是说,该线程进入执行状态??

? notifyall()->删除所有在等待队列中的线程,按线程的优先级执行??

? 不推荐使用notify():因为这是随机地从等待队列中取一个线程执行,我们不能通过设定优先级进行控制,如果,随机抽取的线程不能往前执行,这就有可能产生死锁,所以,建议使用notifyall()??

如:

class MyThread extends Thread{

??? Test t = new Test();

??? public void run(){

??? t.test();

??? System.out.println("Thread say:Hello,World!");

??? }

??? public class Test {

??? int x = 0;

??? public void test(){

??? if(x==0)

??? try{ wait();}catch(Exception e){}

??? }

??? }

??? public static void main(String[] args) throws Exception{

??? new MyThread().start();

??? }

}

结果这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.而如果改成:

public class Test {

????? int x = 0;

????? public synchornized void test(){

????? if(x==0)

????? try{

???? ?wait();

????? }catch(Exception e){}

????? }

????? public static void main(String[] args) throws Exception{

????? new MyThread().start();

????? }

}

我们就可以看到线程一直等待,注意这个线程进入等待后没有其它线程唤醒,除非强行退出JVM环境,否则它一直等待.所以请记住:[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即释放该锁]。

public class ThreadA{???

??? public? static void ?main(String[] args){??

????? ThreadB?? b=new?? ThreadB();??

????? b.start(); //这里只是将线程b处于可运行状态,并不会立刻执行,实际上是先执行主线(进)程,再去执行线程b,如果主线程运行时间过长的话,则会在期间执行b。?

????? System.out.println("b?? is?? start....");??

????? synchronized(b){ //定义一个同步块,使用b作为资源锁。对于wait方法必须有的?????

??????? try{??

????????? System.out.println("Waiting?? for?? b?? to?? complete...");??

????????? b.wait();//临时释放锁,并阻塞当前线程。让其他使用同一把锁的线程ThreadB有机会执行。 注意:直接写wait()是不对的。?

????????? System.out.println("waiting....");??

??????? }catch(InterruptedException e){}??

???? }??

????? System.out.println("Total?? is?? :"+b.total);

???? }???

? }??

? class? ThreadB extends Thread{??

? int?? total;??

? public?? void?? run() {??

????? System.out.println("ThreadB?? is?? running?? ....");??

???? ?synchronized(this){ //必须有

????? System.out.println("ThreadB?? is?? excuting?? for?? statement..");??

????? for(int i=0;i<5;i++){??

???????? total+=i;??

???????? System.out.println("total?? is?? "+total);??

????? }??

???? ?notify();? //在已经获得了一个对象的锁的前提下,调用notify()会通知线程调度程序,唤醒其它等待这个锁的线程队列中的线程,notifyAll()唤醒所有这类线程。

??? }?? //end for synchronized

? }?

? }

运行结果:

b?? is?? start....

Waiting?? for?? b?? to?? complete...

ThreadB?? is?? running?? ....

ThreadB?? is?? excuting?? for?? statement..

total?? is?? 0

total?? is?? 1

total?? is?? 3

total?? is?? 6

total?? is?? 10

waiting....

Total?? is?? :10

?

程序中必须同时满足以下四个条件才会引发死锁:

互斥(Mutual exclusion):线程所使用的资源中至少有一个是不能共享的,它在同一时刻只能由一个线程使用。

持有与等待(Hold and wait):至少有一个线程已经持有了资源,并且正在等待获取其他的线程所持有的资源。

非抢占式(No pre-emption):如果一个线程已经持有了某个资源,那么在这个线程释放这个资源之前,别的线程不能把它抢夺过去使用。

循环等待(Circular wait):假设有N个线程在运行,第一个线程持有了一个资源,并且正在等待获取第二个线程持有的资源,而第二个线程正在等待获取第三个线程持有的资源,依此类推……第N个线程正在等待获取第一个线程持有的资源,由此形成一个循环等待。

举例:

1.未发生死锁

public class Test {???

?? static Object o1 = new Object();//静态对象

?? static Object o2 = new Object();???

??? public static void main(String[] args) {????????

???????? synch synch1 = new synch();

???????? synch synch2 = new synch();

?????? ??synch1.a=0;

???????? synch2.a=2;

???????? Thread t1 = new Thread(synch1);

???????? Thread t2 = new Thread(synch2);

???????? t1.start();

???????? t2.start();

??? }??

}//Test类结束

class synch implements Runnable{???

??? int a;

??? public void run() {

???? ???System.out.println(a);

??????? if(a == 0){

????????? try {

??????????? synchronized (Test.o1) {//对类Testt中的静态对象同步???????????????

??????????????? Thread.sleep(500);

??????????? } //synchronized是对o1的对象锁,500毫秒以后会释放掉对o1的锁,所以不会发生死锁 ????????

??????????? synchronized (Test.o2) {

??????????????? System.out.println("aaaa");

??????????? }

??????? } catch (InterruptedException ex) {}

?????? }else

?????????? try {

??????????????? synchronized (Test.o2) {

?????????????????? Thread.sleep(500);

??????????? }?????????? ?

??????????? synchronized (Test.o1) {

??????????????? System.out.println("aaba");

??????????? }

??????? } catch (InterruptedException ex) {}

???????

??? }

}

运行后显示

0

2

aaba

aaaa

没有发生死锁

2.发生了死锁

修改如下:

public void run() {

??? System.out.println(a);

??? if(a == 0){

??????? try {

??????????? synchronized (Test.o1) { //已获取o1对象锁

??????????????? Thread.sleep(500);

??????????????? System.out.println("Try to get o2");

??????????????? synchronized (Test.o2) {? //未释放o1(同步块中的语句执行完以后才释放该对象的锁),尝试获取o2,被挂起

?????????? ?????????System.out.println("aaaa");

??????????????? }

??????????? }

??????? } catch (InterruptedException ex) {}

??? } else {

??????? try {

??????????? synchronized (Test.o2) { //已获取o2的对象锁

??????????????? Thread.sleep(500);

??????????????? System.out.println("Try to get o1");

??????????????? synchronized (Test.o1) {? //未释放o2,尝试获取o1,也被挂起,死锁

??????????????????? System.out.println("aaba");

??????????????? }

??????????? }

??????? } catch (InterruptedException ex) {}

??? }

}

结果:

0

2

Try to get o2

Try to get o1

另一个例子:

class Friendly {

??? private Friendly partner;

??? private String name;

??? public Friendly(String name) {

??????? this.name = name;

??? }

??? public synchronized void hug() {

??????? System.out.println(Thread.currentThread().getName()+" in " + name + ".hug() trying to invoke " + partner.name +".hugBack()");

??????? partner.hugBack();

??? }

??? private synchronized void hugBack() {

??????? System.out.println(Thread.currentThread().getName()+" in " + name + ".hugBack()");

??? }

??? public void becomeFriend(Friendly partner) {

??????? this.partner = partner;

??? }

public static void main(String[] args) {

??? final Friendly jareth = new Friendly("jareth");

??? final Friendly cory = new Friendly("cory");

??? jareth.becomeFriend(cory);

??? cory.becomeFriend(jareth);

??? new Thread(new Runnable() {

??????? public void run() { jareth.hug(); }

??? }, "Thread1").start();

?

??? new Thread(new Runnable() {

??????? public void run() { cory.hug(); }

??? }, "Thread2").start();

}

}

结果:

Thread1 in jareth.hug() trying to invoke cory.hugBack()

Thread1 in cory.hugBack()

Thread2 in cory.hug() trying to invoke jareth.hugBack()

Thread2 in jareth.hugBack()

读书人网 >编程

热点推荐