天天看点

Java多线程suspend、sleep的控制锁的释放的区别

         因为马上毕业了,最近一直在复习Java的基础知识,多线程当然是重点了,今天上午一直在看线程的生命阶段,其中有过时的方法suspend用来挂起一个线程。而关于该方法为何被抛弃了,看了开发文档中是这么描述的:【方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用

resume

之前锁定该监视器,则会发生死锁。】

看完上面的官方文档,光看文字把头都看晕了,感觉sleep和suspend对挂起线程的操作感觉又模糊了不少,尤其是对锁的控制上面。

因此就模拟了两个线程,代码如下:

package a.b;
public class Test {
	private static final int INDEX = 10;
	public static void main(String[] args) {
		try {
			// 定义线程
			Thread tchild = new Thread(new Runnable() {
				public void run() {
					try {
						int a = 0;
						for (long i = 0; i < 1000000; i++) {
						while (a < INDEX)/** 如果在执行这句的时候调用了tchild.suspend(),那就不会导致死锁 */
							a++;
						}
						System.out.println(i);
						/**
						* 如果正好在执行上面这句的时候调用了tchild.suspend(),
						* 由于println()方法里面保持有锁,
						* 因此,在本线程挂起的时候其他的线程就无法使用println方法中所持有的锁,
						* 这就导致suspend方法容易导致死锁的原因。
						*/
						a = 0;/** 如果在执行这句的时候调用了tchid.suspend(),那就不会导致死锁 *//
					}
				} catch (Throwable e) {e.printStackTrace();}
			}//run
		});
		tchild.start();/**开启子线程*/
		Thread.sleep(2000);/**让主线程停止2s,子线程继续运行2s*/
		tchild.suspend();/**让子线程挂起*/
		
		/**让子线程挂起的情况下再执行主线程中的打印操作*/
		for (long i = 0; i < 1000000; i++) {
			System.out.println("主线程"+i);
		}
		tchild.resume();//激活被挂起的线程 } catch (Throwable ex) { ex.printStackTrace(); } }
}
           

实验结果是:

1)当常量INDEX=10的时候,输出的结果大部分情况下是:运行到:    

....

子线程152463

子线程152464

子线程152465

此时,打印了子线程的信息过了两秒后,子线程被挂起,但是主线程中的内容却没有继续执行,这说明主线程已经被阻塞了;

原因是:子线程在运行到System.out.println(i);这一句的时候被suspend()方法挂起了,而由于该句调用的时候使用了锁,

即out中的常量public final static PrintStream out = null;此处的out对象就是方法println中使用的锁对象;因此在被suspend挂起后子线程任然持有锁-out常量,所以当主线程运行for循环打印信息的时候System.out.println("主线程"+i);,主线程根本拿不到锁,因此造成线程死锁;

2)当常量INDEX=100000000的时候,输出结果大部分情况下是,运行到:

.....

主线程999998

主线程999999

子线程2605

子线程2606

...

子线程999998

子线程999999

,同样的道理因为子线程并不是在运行System.out.println(i);这一句的时候被suspend()方法挂起的,所以就不会持有打印语句中的锁-out常量,因此不会产生死锁现象。

综合上面的分析:

调用suspend方法来挂起一个线程A的时候,如果这好在执行一端具有同步锁的代码块的时候被挂起,那这个同步锁是不会被释放的,那么当线程B执行时,如果内部代码需要使用到该锁,而此时的锁是被线程B中代码块持有的,此时就会导致线程死锁。这就是为什么suspend不安全的地方,因为它无法控制线程内部代码块持有的锁的释放。

关于sleep调用后不会释放锁是指的,当线程A的同步代码块中调用了sleep的使用,该代码块持有的锁不会被释放,那么线程B,C,..去执行具有相同锁对象的代码块的时候就会被阻塞。