这篇文章介绍java死锁机制和线程间通信
死锁
死锁:两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。
同步代码块的嵌套案例
public class MyLock {
// 创建两把锁对象
public static final Object objA = new Object();
public static final Object objB = new Object();
}
public class DieLock extends Thread {
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false); dl1.start();
dl2.start();
}
}
避免死锁
有很多方针可供我们使用来避免死锁的局面:
- 避免嵌套*:这是死锁最主要的原因的,如果你已经有一个资源了就要避免*另一个资源。如果你运行时只有一个对象*,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套*的run()方法,而且程序运行没有死锁局面,运行得很成功。
- 只对有请求的进行*:你应当只想你要运行的资源获取*,比如在上述程序中我在*的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当*住那个特殊的领域而并非完全的对象。
- 避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。
线程间通信
1、Condition newCondition()
返回绑定到此 Lock
实例的新 Condition
实例。在等待条件前,锁必须由当前线程保持。调用Condition.await()
将在等待前以原子方式释放锁,并在等待返回前重新获取锁。
实现注意事项
Condition
实例的具体操作依赖于 Lock
实现,并且该实现必须对此加以记录。
返回:用于此 Lock
实例的新 Condition
实例
2、public interface Condition
Condition
将 Object
监视器方法(wait
、notify
和 notifyAll
)分解成截然不同的对象,以便通过将这些对象与任意 Lock
实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock
替代了 synchronized
方法和语句的使用,Condition
替代了 Object 监视器方法的使用。
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait
做的那样。
Condition
实例实质上被绑定到一个锁上。要为特定 Lock
实例获得 Condition
实例,请使用其 newCondition()
方法。
方法摘要 | |
---|---|
void |
await() 造成当前线程在接到信号或被中断之前一直处于等待状态。 |
boolean |
await(long time, TimeUnit unit) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 |
long |
awaitNanos(long nanosTimeout) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 |
void |
awaitUninterruptibly() 造成当前线程在接到信号之前一直处于等待状态。 |
boolean |
awaitUntil(Date deadline) 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。 |
void |
signal() 唤醒一个等待线程。 |
void |
signalAll() 唤醒所有等待线程。 |
示例:假设创建并启动两个任务,一个用来向账户存款,另一个从同一个账户取款。当取款数额大于账户余额的时,取款线程必须等待。不管什么时候,只要向账户新存了一笔资金,存款线程必须通知提款线程重新尝试。如果余额仍为达到取款数额、提款线程必须继续等待新的存款。
为了同步这些操作,使用一个由条件的锁newDeposit(即增加到账户的新存款)。如果余额小于取款数额,提款任务将等待newDeposit条件。当存款任务给账户增加资金时,存款任务唤醒等待中的提款任务在次尝试。
代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ThreadCooperation {
private static Account account = new Account(); public static void main(String[] args) {
System.out.println("Thread 1\t\tThread 2\t\tBalance"); //创建一个包含两个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new DepositTask());
executor.execute(new WithDrawTask());
executor.shutdown();
} //向账户中存钱的任务
public static class DepositTask implements Runnable {
@Override
public void run() {
try {
while(true) {
account.deposit((int)(Math.random() * 10) + 1);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
} //从账户中取款的任务
public static class WithDrawTask implements Runnable {
@Override
public void run() {
while(true) {
account.withdraw((int)(Math.random() * 10) + 1);
}
}
} public static class Account {
// 创建一个锁
private static Lock lock = new ReentrantLock(); // 创建一个Condition
private static Condition newDeposit = lock.newCondition(); private int balance = 0; public int getBalance() {
return balance;
} public void withdraw(int amount) {
lock.lock();
try {
while (balance < amount) {
System.out.println("\t\t\tWait for a deposit");
newDeposit.await();
}
balance -= amount;
System.out.println("\t\t\tWithdraw " + amount + "\t\t"
+ getBalance());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} public void deposit(int amount) {
lock.lock();
try {
balance += amount;
System.out.println("Deposit " + amount + "\t\t\t\t\t"
+ getBalance());
newDeposit.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} }
}