暂停线程
暂停线程意味着此线程还可以恢复运行。在Java多线程中,suspend()方法可以暂停线程,resume()方法可以恢复线程。
这两个方法都是已经过时的方法。
1.8.1suspend与resume方法的使用
package cn.zxyy.multiThread.Thread.suspendThread;
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++;
}
}
}
package cn.zxyy.multiThread.Thread.suspendThread;
public class MyThreadTest {
public static void main(String[] args) {
try{
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(5000);//主线程睡觉5秒,myThread开始运行,计算i的值,
myThread.suspend();//然后myThread线程暂停了,主线程睡完5秒后,又开始执行就打印下面的i值
System.out.println("A = " + System.currentTimeMillis() + " i = " +myThread.getI());
Thread.sleep(5000);//打印之后又睡5秒,
System.out.println("A = " + System.currentTimeMillis() + " i = " +myThread.getI());//这里我们可以看到i值没有发生变化,说明myThread线程被暂停了
myThread.resume();//使myThread线程恢复运行状态
Thread.sleep(5000);//主线程睡5秒,这时myThread线程在运行
myThread.suspend();
System.out.println("B = " + System.currentTimeMillis() + " i = " +myThread.getI());//再次打印i值发生变化,说明这5秒myThread线程已经被恢复了,并且在运行
Thread.sleep(5000);//主线程再睡5秒,myThread线程被暂停
System.out.println("B = " + System.currentTimeMillis() + " i = " +myThread.getI());//这时再对myThread线程暂停,它又开始不计算值
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
从运行结果上看,在 主线程休眠的5秒钟内,myThread线程一直在运行,主线程休眠完毕后,调用suspend方法使myThread线程暂停,myThread线程暂停运行。主线程再次休眠5秒,这5秒内,myThread线程还处于暂停状态,直到resume方法被调用,myThread线程才重新开始运行。
1.8.2 suspend与resume方法的缺点----独占
使用suspend方法与resume方法时,如果使用不当,会造成公共的同步对象的独占,使得其他线程无法访问公共的同步对象。
package cn.zxyy.multiThread.Thread.suspendThread;
public class SynchronizedObject {
synchronized public void printString(){
System.out.println("begin");
if("a".equals(Thread.currentThread().getName())){
System.out.println("a线程永远suspend了");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
package cn.zxyy.multiThread.Thread.suspendThread;
public class Run2 {
public static void main(String[] args) {
try{
final SynchronizedObject synchronizedObject = new SynchronizedObject();
Thread thread1 = new Thread(){
@Override
public void run() {
synchronizedObject.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(){
@Override
public void run() {
System.out.println("thread2启动了,但是进入不了printString方法,");
System.out.println("因为thread1拿到了synchronized锁,但是一直没有释放");
synchronizedObject.printString();
}
};
thread2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
从运行结果上分析,printString方法时同步方法,也就是说该方法的锁对象是当前的object对象,创建多个线程并启动,当a线程开始执行run方法,打印begin后,a线程进入了暂停状态,这时它还在持有锁,并没有释放锁,所以当b线程启动后,进入run方法,可以执行前两句代码,但是等到执行printString方法的时候,由于a线程没有释放锁,所以线程b拿不到锁,无法进入printString方法。
所以同步方法持有锁对象才能进入,上面的方法时我们自己定义的,可以避免,还有一些Java中提供的代码,可能稍不注意,也会导致独占的情况,举个例子,我们先来看下面的代码执行:
public class MyThread2 extends Thread {
private int i = 0;
@Override
public void run() {
while(true){
i++;
}
}
}
package cn.zxyy.multiThread.Thread.suspendThread;
public class MyThread2Test {
public static void main(String[] args) {
try{
MyThread2 thread = new MyThread2();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("main end ");//打印了main end
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
修改代码再次运行:
public class MyThread2 extends Thread {
private int i = 0;
@Override
public void run() {
while(true){
i++;
System.out.println(i);//如果加上了sout(i),那么就被独占了,因为println方法有synchronized修饰,所以无法释放锁,就不再打印main end了
}
}
}
从上面两个图看运行结果,为什么左图打印了"main end",而右图没有打印呢?
原因是println方法,看看println方法的源码:
从源码中可以看到,println方法内部是同步代码块,也就是说 当thread线程执行到打印语句时,会拿到一个锁,然后thread线程被暂停了,锁没有释放,主线程也有println方法,它这时候是拿不到锁的,所以这里也会出现独占的问题。
1.8.3 suspend与resume方法的缺点----不同步
package cn.zxyy.multiThread.Thread.suspendAndResume;
public class MyObject {
private String username = "1";
private String password = "11";
public void setValue(String u,String p){
this.username = u;
if("a".equals(Thread.currentThread().getName())){
System.out.println("a线程永远暂停了");
Thread.currentThread().suspend();
}
this.password = p;
}
public void printUsernamePassword(){
System.out.println(username+" "+password);
}
}
package cn.zxyy.multiThread.Thread.suspendAndResume;
public class MyObjectTest {
public static void main(String[] args) {
try{
final MyObject obj = new MyObject();
Thread thread1 = new Thread(){
@Override
public void run() {
obj.setValue("a","aa");
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(){
@Override
public void run() {
obj.printUsernamePassword();
}
};
thread2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
从运行结果可以看到,两个线程启动会调用设置值和获取值的方法,出现了脏读的情况,这是因为suspend的方法和resume方法不同步造成的,也就是说当线程a只修改了username的时候,线程b已经运行,获取的值就是新的username,而password还是旧值。
总结:虽然suspend方法和resume方法都已经过时,但是我们需要知道它的缺点:
1.suspend方法暂停线程时,如果有锁的情况不会释放锁,容易造成其他线程无法持有锁,而无法进入run方法。
2.suspend方法和resume方法不是同步方法,可能造成脏读环境。