工学1号馆

home

java多线程系列5-死锁与线程间通信

Wu Yudong    June 19, 2015     Java   582   

原创文章,转载请注明: 转载自工学1号馆

这篇文章介绍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();
	}
}

关于死锁的问题在《java多线程系列3-线程同步》中有过探讨

避免死锁

有很多方针可供我们使用来避免死锁的局面:

  • 避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。
  • 只对有请求的进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。
  • 避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。

线程间通信

1、Condition  newCondition()

返回绑定到此 Lock 实例的新 Condition 实例。在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。

实现注意事项

Condition 实例的具体操作依赖于 Lock 实现,并且该实现必须对此加以记录。

返回:用于此 Lock 实例的新 Condition 实例

2、public interface Condition

ConditionObject 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意 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(); // 释放锁
			}
		}

	}
}

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)