死锁的影响
几率不高单危害大
/**
* 必定发生死锁
*/
public class MustDeadLock implements Runnable{
int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
public static void main(String[] args) {
MustDeadLock r1 = new MustDeadLock();
MustDeadLock r2 = new MustDeadLock();
r1.flag = 1;
r2.flag = 0;
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("flag = "+flag);
if(flag == 1){
synchronized (o1){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
System.out.println("线程1成功拿到两把锁");
}
}
}
if(flag == 0){
synchronized (o2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("线程2成功拿到两把锁");
}
}
}
}
}
/**
* 转账时候遇到死锁,一旦打开注释,就是发生死锁
*/
public class TransferMoney implements Runnable {
private int flag = 1;
static Account a = new Account(500);
static Account b = new Account(500);
public static void main(String[] args) throws InterruptedException {
TransferMoney r1 = new TransferMoney();
TransferMoney r2 = new TransferMoney();
r1.flag = 1;
r2.flag = 0;
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("a的余额"+a.balance);
System.out.println("b的余额"+b.balance);
}
@Override
public void run() {
if (flag == 1) {
transferMoney(a, b, 200);
}
if (flag == 0) {
transferMoney(b, a, 200);
}
}
public static void transferMoney(Account from, Account to, int amount) {
synchronized (from) {
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (to) {
if (from.balance - amount < 0) {
System.out.println("余额不足,转账结束。");
}
from.balance -= amount;
to.balance += amount;
System.out.println("成功转账"+amount +"元");
}
}
}
static class Account {
public Account(int balance) {
this.balance = balance;
}
int balance;
}
}
/**
* 多人同时转账,依然很危险(演示多人转账死锁)
*/
public class MultiTransferMoney {
private static final int NUM_ACCOUNRS = 5000;
private static final int NUM_MONEY = 1000;
private static final int NUM_ITERATIONS = 1000000;
private static final int NUM_THREADS = 20;
public static void main(String[] args) {
Random rnd = new Random();
TransferMoney.Account[] accounts = new TransferMoney.Account[NUM_ACCOUNRS];
for(int i =0; i
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
/**
* 用ThreadMXBean检测死锁
*/
public class ThreadMXBeanDetection implements Runnable{
int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
public static void main(String[] args) throws InterruptedException {
MustDeadLock r1 = new MustDeadLock();
MustDeadLock r2 = new MustDeadLock();
r1.flag = 1;
r2.flag = 0;
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
Thread.sleep(1000);
//用ThreadMXBean检测死锁
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
//拿到所有死锁线程id
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if(deadlockedThreads != null && deadlockedThreads.length > 0){
for(int i = 0; i < deadlockedThreads.length ; i++){
ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
System.out.println("发现死锁了"+threadInfo.getThreadName());
}
}
}
@Override
public void run() {
System.out.println("flag = "+flag);
if(flag == 1){
synchronized (o1){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
System.out.println("线程1成功拿到两把锁");
}
}
}
if(flag == 0){
synchronized (o2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("线程2成功拿到两把锁");
}
}
}
}
}
输出结果
flag = 1
flag = 0
发现死锁了Thread-1
发现死锁了Thread-0
/**
* 修复转账死锁问题
*/
public class TransferMoney implements Runnable {
private int flag = 1;
static Account a = new Account(500);
static Account b = new Account(500);
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
TransferMoney r1 = new TransferMoney();
TransferMoney r2 = new TransferMoney();
r1.flag = 1;
r2.flag = 0;
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("a的余额" + a.balance);
System.out.println("b的余额" + b.balance);
}
@Override
public void run() {
if (flag == 1) {
transferMoney(a, b, 200);
}
if (flag == 0) {
transferMoney(b, a, 200);
}
}
public static void transferMoney(Account from, Account to, int amount) {
//编写一个帮助类
class Helper {
public void transfer() {
if (from.balance - amount < 0) {
System.out.println("余额不足,转账结束。");
}
from.balance -= amount;
to.balance += amount;
System.out.println("成功转账" + amount + "元");
}
}
//获取两个对象的hashCode
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
//根据hash值大小不同加锁顺序调换
if (fromHash < toHash) {
synchronized (from) {
synchronized (to) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (to) {
synchronized (from) {
new Helper().transfer();
}
}
} else {//当hash值一样
synchronized (lock){
synchronized (to) {
synchronized (from) {
new Helper().transfer();
}
}
}
}
}
static class Account {
public Account(int balance) {
this.balance = balance;
}
int balance;
}
}
有死锁和耗尽资源风险
代码演示:哲学家进入死锁
死锁:每个哲学家都拿起左手边的筷子,永远都在等右边的筷子(或者相反)
/**
* 演示哲学家就餐导致死锁
*/
public class DiningPhilosophers {
public static class Philosopher implements Runnable {
private Object leftChopstick;
private Object rightChopstick;
public Philosopher(Object leftChopstick, Object rightChopstick) {
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
}
@Override
public void run() {
try {
while (true) {
doAction("Thinking");
synchronized (leftChopstick) {
doAction("Picked up left chopstick");
synchronized (rightChopstick) {
doAction("Picked up right chopstick -eating");
doAction("Picked down right chopstick");
}
doAction("Picked down left chopstick");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doAction(String action) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + " " + action);
Thread.sleep((long) (Math.random() * 10));
}
}
public static void main(String[] args) {
Philosopher[] philosophers = new Philosopher[5];
Object[] chopsticks = new Object[philosophers.length];
for (int i = 0; i < chopsticks.length; i++) {
chopsticks[i] = new Object();
}
for (int i = 0; i < philosophers.length; i++) {
Object leftChopstick = chopsticks[i];
Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];
philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
new Thread(philosophers[i], "哲学家" + (i + 1) + "号").start();
}
}
}
众多结果里的其中一种结果:
哲学家1号 Thinking
哲学家3号 Thinking
哲学家4号 Thinking
哲学家2号 Thinking
哲学家5号 Thinking
哲学家2号 Picked up left chopstick
哲学家4号 Picked up left chopstick
哲学家1号 Picked up left chopstick
哲学家3号 Picked up left chopstick
哲学家5号 Picked up left chopstick
/**
* 避免策略:哲学家就餐的换手方案
* b. 改变一个哲学家拿筷子的顺序(避免策略)
* 只贴出主方法,因为其他代码合上面的一致
* 主要看这个if里的初始化哲学家的代码 if(i == philosophers.length - 1){
*/
public static void main(String[] args) {
Philosopher[] philosophers = new Philosopher[5];
Object[] chopsticks = new Object[philosophers.length];
for (int i = 0; i < chopsticks.length; i++) {
chopsticks[i] = new Object();
}
for (int i = 0; i < philosophers.length; i++) {
Object leftChopstick = chopsticks[i];
Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];
if(i == philosophers.length - 1){
//让其中一个哲学家拿筷子的顺序相反
philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
}else{
philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
}
new Thread(philosophers[i], "哲学家" + (i + 1) + "号").start();
}
}
实际工程中如何避免死锁
设置超时时间
多使用并发类而不是自己设计锁
降低锁的力度:用不同的锁,而不是一个锁。
如果能使用同步代码块,就不使用同步方法:自己指定锁的对象。
给你的线程起个有意义的名字:debug和排查时事半功倍,框架和JDK都遵循这个最佳实践。
避免锁的嵌套:如上面的MustDeadLock 类。
分配资源前先看能不能收回来:银行家算法
尽量不要几个功能使用同一把锁:专锁专用
Lock的tryLock避免死锁的代码演示
/**
* 用tryLock避免死锁
*/
public class TryLockDeadLock implements Runnable{
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
private int flag = 1;
public static void main(String[] args) {
TryLockDeadLock r1 = new TryLockDeadLock();
TryLockDeadLock r2 = new TryLockDeadLock();
r1.flag = 1;
r1.flag = 0;
new Thread(r1).start();
new Thread(r2).start();
}
@Override
public void run() {
for(int i=0;i<100;i++){
if(flag == 1){
try {
if(lock1.tryLock(800, TimeUnit.MILLISECONDS)){
System.out.println("线程1获取到了锁1");
if(lock2.tryLock(800,TimeUnit.MILLISECONDS)){
System.out.println("线程1获取到了锁2");
System.out.println("线程1获取到了两把锁");
lock2.unlock();
lock1.unlock();
break;
}else {
System.out.println("线程1尝试锁2失败,已重试");
lock1.unlock();
Thread.sleep(new Random().nextInt(1000));
}
}else{
System.out.println("线程1获取锁1失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(flag == 0){
try {
if(lock2.tryLock(3000, TimeUnit.MILLISECONDS)){
System.out.println("线程2获取到了锁2");
if(lock1.tryLock(3000,TimeUnit.MILLISECONDS)){
System.out.println("线程2获取到了锁1");
System.out.println("线程2获取到了两把锁");
lock2.unlock();
lock1.unlock();
break;
}else {
System.out.println("线程2尝试锁1失败,已重试");
lock1.unlock();
Thread.sleep(new Random().nextInt(1000));
}
}else{
System.out.println("线程2获取锁2失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
上篇:单例模式的8种写法
下篇:活跃性问题(活锁、饥饿)