1.死锁
(1). 概念 : 不同的线程分别占据着对方的资源不放弃,都在等待对方放弃自己需要的资源,这样就形成了死锁.
一旦出现死锁,程序即不会给出异常,也不会给出提示信息,只是所有线程处于阻塞状态,无法进行.
(2). 例子 : 一个人要一双筷子才能吃饭,而A和B各只有一根筷子,A说B把筷子给我我才能吃饭,B说A把筷子给我我才可以吃饭......
如 :
public class DeadLock { public static void main(String[] args) { StringBuilder b1 = new StringBuilder(); StringBuilder b2 = new StringBuilder(); new Thread(new Runnable(){ @Override public void run() { synchronized (b1) { b1.append("我不是"); b2.append("我是"); } synchronized (b2){ b1.append("fw"); b2.append("fw"); } } }).start(); System.out.println(b1); System.out.println(b2); } } 控制台无输出
(3). 诱发死锁原因
- 互斥条件
- 占用且等待
- 不可抢占
- 循环等待
满足以上四个条件将会发生死锁.
(4). 如何解决 :
死锁一旦出现,基本很难人为干预,只能尽量规避.
- 互斥条件基本上无法破坏.因为线程需要互斥来解决安全问题.
- 可以考虑一次性申请所有所需的资源,这样就不存在等待的问题.
- 占用部分资源的线程在进一步申请其他资源时,如果申请不到,就将主动释放自己的占用的资源.
- 可以将资源改为线性顺序.申请资源时,先申请序号较小的,避免循环等问题.
2.锁(Lock)(接口)
JDK5.0新增的特性,保证线程安全.与synchronized相比,锁可以提供多种方案.,更灵活强大.锁通过显式定义同步锁对象实现同步(ReentrantLock类的对象).同步锁使用Lock充当.
使用锁处理线程安全问题步骤 :
- 创建Lock的实例,确保多个线程共用同一个锁的实例.考虑将此对象声明为static final.
- 执行lock方法,锁定对资源的调用.
- 执行unlock方法,释放对共享数据的锁定.
public class ReentrantlockTest { public static void main(String[] args) { D d = new D(); Thread t1 = new Thread(d, "线程-1"); Thread t2 = new Thread(d, "线程-2"); t1.start(); t2.start(); } } class D implements Runnable{ int change = 100; public static final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true) { try{ lock.lock(); if (change > 0) { System.out.println(Thread.currentThread().getName() + "\t" + change); change--; } else { break; } }finally{ lock.unlock(); } } } } 控制台 线程-1 100 线程-1 99 线程-1 98 线程-1 97 线程-1 96 线程-1 95 线程-1 94 线程-1 93 线程-1 92
3.Lock与synchronized同步方式的对比
- synchronized不管是同步方法还是同步代码块,在一对花括号结束之后,都需要释放对同步监视器的调用.
- Lock通过两个方法来控制需要被同步的方法,更灵活.
- Lock作为接口,提供了多种实现类,更适用于更加复杂的场景.