Java多线程suspend、sleep的控制锁的释放的区别
2015-09-28 22:05
204 查看
因为马上毕业了,最近一直在复习Java的基础知识,多线程当然是重点了,今天上午一直在看线程的生命阶段,其中有过时的方法suspend用来挂起一个线程。而关于该方法为何被抛弃了,看了开发文档中是这么描述的:【方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用
看完上面的官方文档,光看文字把头都看晕了,感觉sleep和suspend对挂起线程的操作感觉又模糊了不少,尤其是对锁的控制上面。
因此就模拟了两个线程,代码如下:
实验结果是:
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,..去执行具有相同锁对象的代码块的时候就会被阻塞。
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,..去执行具有相同锁对象的代码块的时候就会被阻塞。
相关文章推荐
- SpringMVC配置JSON、JSP、FreeMark多视图解析器配置
- SpringTask定时器
- Java类权限(转)
- Eclipse-Java常见异常/错误解决
- Java虚拟机9:Java类加载机制
- Java基础知识强化之IO流笔记09:File类功能
- 内部类
- java中string对象赋值
- spring 构造注入
- 用Notepad的NppExc插件运行带包名的java文件
- Java基础知识强化之IO流笔记08:异常的注意事项
- ID3、C4.5算法介绍以及java代码实现
- 控制逻辑的分离——springMVC
- spring helloworld
- java (web)异常分析java.lang.ClassNotFoundException: Aservlet
- java周期调度几种实现
- JAVA泛型方法的声明与实现
- Java基础知识强化之IO流笔记07:自定义的异常概述和自定义异常实现
- GsonFormat快速实现JavaBean
- Java中的Iterator