suspend() 与 resume() 方法
本节通过一个案例来介绍 suspend() 与 resume() 方法的用法。首先来看一下案例中使用到的 MyThread21 线程,代码如下所示。
package ch14; public class MyThread21 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++; } } }
MyThread21 线程中有一个成员 i,其中 setI() 方法和 getI() 方法分别用于设置和获取 i 的值,run() 方法则是一个从i开始递增的死循环。
下面编写主线程的代码,具体如下所示。
package ch14; public class Test25 { public static void main(String[] args) { try { MyThread21 thread=new MyThread21(); 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(); } } }
最终运行结果如下所示。
A= 1540978346179 i= 2680986095 A= 1540978351179 i= 2680986095 B= 1540978356179 i= 5348657508 B= 1540978361179 i= 5348657508
从输出结果的时间来看,调用 suspend() 方法确实可以暂停线程,而在调用 resume() 方法后线程恢复运行状态。
独占问题
在使用 suspend() 方法与 resume() 方法时,如果使用不当极易造成公共的同步对象被独占,从而使得其他线程无法访问公共同步对象。
例 2
下面通过一个案例来演示这种情况。如下所示是案例中使用的公共对象的代码。
package ch14; public class SynchronizedObject1 { 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"); } }
SynchronizedObject1 类在 printString() 方法开始时输出“begin”,在该方法结束时输出“end”,方法体中的if判断如果当前线程的名称是 a 则调用 suspend() 方法暂停线程。
接下来编写主线程代码,在主线程中创建一个 SynchronizedObject1 类对象并在线程中调用 printString() 方法,具体代码如下所示。
package ch14; public class Test26 { public static void main(String[] args) { try { final SynchronizedObject1 object=new SynchronizedObject1(); 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线程锁定并且永远暂停了!"); object.printString(); } }; thread2.start(); } catch(InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
上述代码比较简单这里就不再解释,运行后的结果如下所示。从中可以看到由于线程被永久暂停,所以只会输出一个 begin。
begin a线程永远 suspend了! thread2启动了,但进入不了printString()方法!所以只会打印1个begin! 因为printString()方法被a线程锁定并且永远暂停了!
例 3
还有另外一种独占锁的情况也要格外注意,稍有不慎就会掉进“坑”里。创建测试用的 MyThread22 线程,具体代码如下:
package ch14; public class MyThread22 extends Thread { private long i=0; @Override public void run() { while (true) { i++; } } }
再来看主线程的代码,如下所示。
package ch14; public class Test27 { public static void main(String[] args) { try { MyThread22 thread=new MyThread22(); thread.start(); Thread.sleep(1000); thread.suspend(); System.out.println("main end!"); } catch(InterruptedException e) { e.printStackTrace(); } } }
程序执行后将看到预料不到的结果,如下所示。
main end!
如果将 MyThread22 线程类的代码更改如下:
package ch14; public class MyThread22 extends Thread { private long i=0; @Override public void run() { while(true) { i++; System.out.println(i); } } }
再次运行程序,控制台将不打印 main end,运行结果如下所示。
...... 130862 130863 130864 130865 130866 130867
出现这种情况的原因是,当程序运行到 println() 方法内部停止时,同步锁未被释放。这导致当前 PrintStream 对象的 println() 方法一直呈“暂停”状态,并且“锁未释放”,而 main() 方法中的代码“System.out. println(Mmain end!'1);”迟迟不能执行打印。
提示:虽然 suspend() 方法是过期作废的方法,但还是有必要研究它过期作废的原因,这是很有意义的。
不同步问题
在使用 suspend() 方法与 resume() 方法时也容易出现因为线程的暂停而导致数据不同步的情况。
例 4
下面通过一个案例来演示这种情况。如下所示是案例中使用的公共对象的代码。
package ch14; 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); } }
如上述代码所示,MyObject 类的 setValue() 方法会在线程名称是 a 时执行停止线程操作。 如下所示是主线程代码。
package ch14; public class Test28 { 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(); } }
程序运行结果如下所示。
停止a线程! a 11
从程序运行的结果可以看到,出现了值不同步的情况,所以在程序中使用 suspend() 方法要格外注意。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/21966.html