全国报名热线

15201841284

首页>Java>正文

Java如何暂停线程及停止线程,释放锁的不良后果及异常_上海java培训

时间:2018-06-04 20:51:07   来源:上海尚学堂   阅读:

之前我们讲过进程和线程一些东西,包括如何终止线程,点击回顾:
Java进程和Java多线程的概念和优点_上海java培训
Java多线程的使用_上海java培训
Java如何停止线程?Java终止线程的几种方法解析_上海Java培训

接下来本文介绍以下内容
1、方法stop() 与java.lang.ThreadDeath 异常
2、释放锁的不良后果
3、使用return 停止线程
4、如何暂停线程

 

一、方法stop() 与java.lang.ThreadDeath 异常


调用 stop() 方法时会抛出 java.lang.ThreadDeath 异常,但在通常的情况下,此异常不需要显式地捕捉。

创建测试用的项目 runMethodUseStopMethod,文件 MyThread.java 代码如下:
package testpackage;
public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            this.stop();
        } catch (ThreadDeath e) {
 System.out.println(" 进入了 catch() 方法! ");
            e.printStackTrace();
        }
    }
}
文件 Run.java 代码如下:
package test.run;
import testpackage.MyThread;
public class Run {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}
 
 
 
 
程序运行后的效果如下图所示。


方法 stop() 已经被作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。

 

二、释放锁的不良后果
 

使用 stop() 释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误,一定要特别注意。来看一个示例。

创建项目 stopThrowLock,文件 SynchronizedObject.java 代码如下:
package testpackage;
public class SynchronizedObject {
    private String username = "a";
    private String password = "aa";
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    synchronized public void printString(String username, String password) {
        try {
            this.username = username;
            Thread.sleep(100000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
文件 MyThread.java 代码如下:
package testpackage;
public class MyThread extends Thread {
    private SynchronizedObject object;
    public MyThread(SynchronizedObject object) {
        super();
        this.object = object;
    }
    @Override
    public void run() {
        object.printString("b", "bb");
    }
}
文件 Run.java 代码如下:
package test.run;
import testpackage.MyThread;
import testpackage.SynchronizedObject;
public class Run {
    public static void main(String[] args) {
        try {
            SynchronizedObject object = new SynchronizedObject();
            MyThread thread = new MyThread(object);
            thread.start();
            Thread.sleep(500);
            thread.stop();
            System.out.println(object.getUsername() + " "
                    + object.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
程序运行结果下图所示。



 
 
由于 stop() 方法已经在 JDK 中被标明是“作废 / 过期”的方法,显然它在功能上具有缺陷,所以不建议在程序中使用 stop() 方法。


 

三、使用return 停止线程

将方法 interrupt() 与 return 结合使用也能实现停止线程的效果。创建测试用的项目 useReturnInterrupt,线程类 MyThread.java 代码如下:

package extthread;
public class MyThread extends Thread {
 
    @Override
    public void run() {
            while (true) {
                if (this.isInterrupted()) {
 System.out.println(" 停止了 !");
                    return;
                }
                System.out.println("timer=" + System.currentTimeMillis());
            }
    }
}

运行类 Run.java 代码如下:
package test.run;
import extthread.MyThread;
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread t=new MyThread();
        t.start();
        Thread.sleep(2000);
        t.interrupt();
    }
}
 
程序运行结果如下图 所示。



 
不过还是建议使用“抛异常”的方法来实现线程的停止,因为在 catch 块中还可以将异常向上抛,使线程停止的事件得以传播。
 

 

四、暂停线程

暂停线程意味着此线程还可以恢复运行。在 Java 多线程中,可以使用 suspend() 方法暂停线程,使用 resume() 方法恢复线程的执行。

 

1、suspend 与resume 方法的使用

本节将讲述如何使用 suspend 与 resume 方法。

创建测试用的项目 suspend_resume_test,文件 MyThread.java 代码如下:
package mythread;
public class MyThread extends Thread {
    private long i = 0;
    public long getI() {
        return i;
    }
    public void setI(long i) {
        this.i = i;
    }
    @Override
    public void run() {
        while (true) {
            i++;
        }
    }
}
 
文件 Run.java 代码如下:
package test.run;
import mythread.MyThread;
public class Run {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(5000);
 // A 段
            thread.suspend();
            System.out.println("A= " + System.currentTimeMillis() + " i="
                    + thread.getI());
            Thread.sleep(5000);
            System.out.println("A= " + System.currentTimeMillis() + " i="
                    + thread.getI());
 // B 段
            thread.resume();
            Thread.sleep(5000);
 // C 段
            thread.suspend();
            System.out.println("B= " + System.currentTimeMillis() + " i="
                    + thread.getI());
            Thread.sleep(5000);
            System.out.println("B= " + System.currentTimeMillis() + " i="
                    + thread.getI());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序运行后的结果如下图所示。


  暂停与恢复线程

 
从控制台打印的时间上来看,线程的确被暂停了,而且还可以恢复成运行的状态。
 
 
 

2、suspend 与resume 方法的缺点——独占
 

在使用 suspend 与 resume 方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。
创建 suspend_resume_deal_lock 项目,文件 SynchronizedObject.java 代码如下:
package testpackage;
public class SynchronizedObject {
    synchronized public void printString() {
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a")) {
 System.out.println("a 线程永远 suspend 了! ");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}
文件 Run.java 代码如下:
package test.run;
import testpackage.SynchronizedObject;
public class Run {
    public static void main(String[] args) {
        try {
            final SynchronizedObject object = new SynchronizedObject();
            Thread thread1 = new Thread() {
                @Override
                public void run() {
                    object.printString();
                }
            };
            thread1.setName("a");
            thread1.start();
            Thread.sleep(1000);
            Thread thread2 = new Thread() {
                @Override
                public void run() {
                    System.out
 .println("thread2 启动了,但进入不了 printString() 方法!
只打印 1 个 begin");
                    System.out
 .println(" 因为 printString() 方法被 a 线程锁定并且永远
suspend 暂停了! ");
                    object.printString();
                }
            };
            thread2.start();
        } catch (InterruptedException e) {
 
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

程序运行后的结果如左图所示。

   左图:独占并锁死了 printString 方法        右图:控制台打印 main end 信息
 
还有另外一种独占锁的情况也要格外注意,稍有不慎,就会掉进“坑”里。创建测试用
的项目 suspend_resume_LockStop,类 MyThread.java 代码如下:
package mythread;
public class MyThread extends Thread {
    private long i = 0;
    @Override
    public void run() {
        while (true) {
            i++;
        }
    }
}
类 Run.java 代码如下:
package test.run;
import mythread.MyThread;
public class Run {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(1000);
            thread.suspend();
            System.out.println("main end!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
程序运行后顺序打印的信息如上面右图 1-47 所示。
 
 
但如果将线程类 MyThread.java 更改如下:
 
package mythread;
public class MyThread extends Thread {
    private long i = 0;
    @Override
    public void run() {
        while (true) {
            i++;
            System.out.println(i);
        }
    }
}
再次运行程序,控制台将不打印 main end,运行结果如下图 所示。

    不打印 main end 信息



出现这样情况的原因是,当程序运行到 println() 方法内部停止时,同步锁未被释放。方
法 println() 源代码如图 所示。

 锁不被释放

 
这导致当前 PrintStream 对象的 println() 方法一直呈“暂停”状态,并且“锁未释放”,而 main() 方法中的代码 System.out.println("main end!"); 迟迟不能执行打印。虽然 suspend() 方法是过期作废的方法,但还是有必要研究它过期作废的原因,这是很有意义的。

 

3、suspend 与resume 方法的缺点——不同步

在使用 suspend 与 resume 方法时也容易出现因为线程的暂停而导致数据不同步的情况。创建项目 suspend_resume_nosameValue,文件 MyObject.java 代码如下:

package myobject;
public class MyObject {
    private String username = "1";
    private String password = "11";
    public void setValue(String u, String p) {
        this.username = u;
        if (Thread.currentThread().getName().equals("a")) {
 System.out.println(" 停止 a 线程! ");
            Thread.currentThread().suspend();
        }
        this.password = p;
    }
    public void printUsernamePassword() {
        System.out.println(username + " " + password);
    }
}
文件 Run.java 代码如下:
package test;
import myobject.MyObject;
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final MyObject myobject = new MyObject();
        Thread thread1 = new Thread() {
            public void run() {
                myobject.setValue("a", "aa");
            };
        };
        thread1.setName("a");
        thread1.start();
        Thread.sleep(500);
        Thread thread2 = new Thread() {
            public void run() {
                myobject.printUsernamePassword();
            };
        };
        thread2.start();
    }
}

程序运行结果如下图 所示。


    最后运行结果
 
程序运行的结果出现值不同步的情况,所以在程序中使用 suspend() 方法要格外注意。关于如何解决这些问题,以后再说。

感谢阅读上海尚学堂Java培训文章,更多z学习资料请l联系咨询。
Java线程有优先级,具体参看《Java线程优先级设置介绍及其特性-上海Java培训
分享:0