Java 死锁
2016-07-28 17:32
399 查看
我参与的几个项目中,死锁貌似未做太多考虑,可能项目对并发的安全和性能要求不高吧。但是在面试的时候,死锁的问题依然常常被提起。这里就简单介绍下造成死锁的原因及避免方法。
假如有2个线程,一个线程想先锁对象1,再锁对象2,恰好另外有一个线程先锁对象2,再锁对象1。
在这个过程中,当线程1把对象1锁好以后,就想去锁对象2,但是不巧,线程2已经把对象2锁上了,也正在尝试去锁对象1。
什么时候结束呢,只有线程1把2个对象都锁上并把方法执行完,并且线程2把2个对象也都锁上并且把方法执行完毕,那么就结束了,但是,谁都不肯放掉已经锁上的对象,所以就没有结果,这种情况就叫做线程死锁。下面有个实例程序模拟死锁:
package org.thread;
public class Synchron {
public void begin() {
Thread t1 = new Thread(new Thread1());
Thread t2 = new Thread(new Thread2());
t1.start();
t2.start();
}
public static void main(String[] args) {
new Synchron().begin();
}
public synchronized void getI() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
getJ();
}
public synchronized void getJ() {
getI();
}
class Thread1 implements Runnable {
public void run() {
getI();
}
}
class Thread2 implements Runnable {
public void run() {
getJ();
}
}
}
那么我们需要注意的是,线程死锁的原因不是因为相互调用,而是由线程对资源的占用和等待导致的!
有一种避免死锁的办法是,尽可能锁大的对象,即加大锁的粒度;也应避免同时锁多个对象。
再简单说说wait和notify:
1).wait
Object类中的final方法,有InterruptedException。它的作用是导致当前的线程等待,直到其它线程调用此对象的notify方法或者notifyAll方法,wait还有一些重用方法,传参数,比如说时间长度。
当前的线程必须拥有此对象监视器,然后该线程发布对此监视器的所有权并且开始等待,直到其它线程通过调用notify方法或者notifyAll方法,通知在此对象的监视器上等待的线程醒来,然后该线程将等到重新获得对监视器的所有权后才能开始执行。
说说wait和sleep的区别
首先sleep
sleep是Thread里面的方法,在被执行的时候,锁并不会被交出去,要直到sleep所在的方法全部被执行完毕以后才交出锁。
wait是Object里面的方法,在被执行的时候,锁被解除,由其它线程去争夺,直到有notify或者notifyAll方法唤醒它。
2).Notify
也是Object类中的方法,用于唤醒在此对象上等待着的某一个线程,如果有很多线程挂起的话,就随机地决定哪一个。注意,是随机的,这时可以用notifyAll来唤醒所有的。一定要注意这个问题,除非你明确地知道你在做什么,否则最好就是用notifyAll。
注意事项:
wait()和notify()必须包括在synchronized代码块中,等待中的线程必须由notify()方法显式地唤醒,否则它会永远地等待下去。很多人初级接触多线程时,会习惯把wait()和notify()放在run()方法里,一定要谨记,这两个方法属于某个对象,应在对象所在的类方法中定义它,然后run中去调用它。
假如有2个线程,一个线程想先锁对象1,再锁对象2,恰好另外有一个线程先锁对象2,再锁对象1。
在这个过程中,当线程1把对象1锁好以后,就想去锁对象2,但是不巧,线程2已经把对象2锁上了,也正在尝试去锁对象1。
什么时候结束呢,只有线程1把2个对象都锁上并把方法执行完,并且线程2把2个对象也都锁上并且把方法执行完毕,那么就结束了,但是,谁都不肯放掉已经锁上的对象,所以就没有结果,这种情况就叫做线程死锁。下面有个实例程序模拟死锁:
package org.thread;
public class Synchron {
public void begin() {
Thread t1 = new Thread(new Thread1());
Thread t2 = new Thread(new Thread2());
t1.start();
t2.start();
}
public static void main(String[] args) {
new Synchron().begin();
}
public synchronized void getI() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
getJ();
}
public synchronized void getJ() {
getI();
}
class Thread1 implements Runnable {
public void run() {
getI();
}
}
class Thread2 implements Runnable {
public void run() {
getJ();
}
}
}
那么我们需要注意的是,线程死锁的原因不是因为相互调用,而是由线程对资源的占用和等待导致的!
有一种避免死锁的办法是,尽可能锁大的对象,即加大锁的粒度;也应避免同时锁多个对象。
再简单说说wait和notify:
1).wait
Object类中的final方法,有InterruptedException。它的作用是导致当前的线程等待,直到其它线程调用此对象的notify方法或者notifyAll方法,wait还有一些重用方法,传参数,比如说时间长度。
当前的线程必须拥有此对象监视器,然后该线程发布对此监视器的所有权并且开始等待,直到其它线程通过调用notify方法或者notifyAll方法,通知在此对象的监视器上等待的线程醒来,然后该线程将等到重新获得对监视器的所有权后才能开始执行。
说说wait和sleep的区别
首先sleep
sleep是Thread里面的方法,在被执行的时候,锁并不会被交出去,要直到sleep所在的方法全部被执行完毕以后才交出锁。
wait是Object里面的方法,在被执行的时候,锁被解除,由其它线程去争夺,直到有notify或者notifyAll方法唤醒它。
2).Notify
也是Object类中的方法,用于唤醒在此对象上等待着的某一个线程,如果有很多线程挂起的话,就随机地决定哪一个。注意,是随机的,这时可以用notifyAll来唤醒所有的。一定要注意这个问题,除非你明确地知道你在做什么,否则最好就是用notifyAll。
注意事项:
wait()和notify()必须包括在synchronized代码块中,等待中的线程必须由notify()方法显式地唤醒,否则它会永远地等待下去。很多人初级接触多线程时,会习惯把wait()和notify()放在run()方法里,一定要谨记,这两个方法属于某个对象,应在对象所在的类方法中定义它,然后run中去调用它。
相关文章推荐
- 线程死锁
- 死锁
- java多线程-java死锁
- java死锁问题定位
- JAVA最直观的死锁代码
- 多线程死锁的一个简单例子
- java死锁
- Java多线程死锁的产生原因以及如何避免
- springmvc 用户登出/注销 controller,并清空密码
- 内存溢出的解决方案
- Java8 时间处理
- 线程池模式比较-------ICE线程池模型------L/F领导者跟随者模式
- 解决使用Idea/Eclipse编写Hadoop程序包依赖问题
- Selenium2+java 环境搭建
- JAVA总结抽象类与接口
- Java IO流分析整理
- 关于java反射机制
- spring+mybatis 数据源读取不到配置文件的值
- Java NIO 简单了解
- Spring Bean的作用域