java多线程系列5-死锁与线程间通信
这篇文章介绍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(); // 释放锁
}
} }
}
java多线程系列5-死锁与线程间通信的更多相关文章
- Java多线程学习(五)线程间通信知识点补充
系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...
- Java多线程编程(三)线程间通信
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...
- C++多线程编程(三)线程间通信
多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...
- Java多线程系列 基础篇02 线程的创建和运行
1.线程创建的方式常用有两种 1. 继承 Thread 类创建线程 2. 实现 Runnable 接口创建线程 2.Thread 和 Runnable的区别 Thread和Runnable的相同点:都 ...
- 【JAVA并发第三篇】线程间通信
线程间的通信 JVM在运行时会将自己管理的内存区域,划分为不同的数据区,称为运行时数据区.每个线程都有自己私有的内存空间,如下图示: Java线程按照自己虚拟机栈中的方法代码一步一步的执行下去,在这一 ...
- java常见面试题3:线程间通信
写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z. 打印顺序为12A34B56C78D--5152Z.要求用线程间的通信. 代码清单: class Printer { private in ...
- Java多线程系列 基础篇03 线程的优先级和守护线程
1. 线程优先级 现代操作系统中基本上使用时间分片的方式调度线程,通过设置线程优先级,使优先级高的线程获得时间片的次数多于优先级低的线程. 在java 线程中,通过一个整形变量prority来控制优先 ...
- Java多线程系列 基础篇01 线程的状态
1.进程和线程 进程: 计算机中程序关于某数据集合的一次运行活动,是计算机系统进行资源分配和调度的基本单位,是操作系统结构的基础. 线程: 线程是进程的实例,是CPU进行资源分配和调度的最小单位,线程 ...
- (Java多线程系列五)守护线程
守护线程 什么是守护线程 Java中有两种线程,一种是用户线程,一种是守护线程. 当进程不存在或主线程停止,守护线程也会自动停止. class DaemonThread extends Thread ...
随机推荐
- Docker练习例子:基于 VNCServer + noVNC 构建 Docker 桌面系统
0. 安装docker 这一步略,网上有好多教程,一般出现装不上的原因,也就是网速问题了,这个我也很难帮你. 1. 下载指定的镜像images docker pull dorowu/ubuntu-de ...
- 超实用的JavaScript技巧及最佳实践(上)
在这篇文章中,作者将会向大家分享JavaScript开发的小技巧.最佳实践等非常实用的内容,不管你是前端开发者还是服务端开发者,都应该来看看这些小技巧,它们绝对会让你受益的. 文中所提供的代码片段都已 ...
- C#中,接口不能被实例化,但存在特例
看一个例子: interface IFoo { string Message { get; } } 则, IFoo obj = new IFoo("abd"); 将会报错:接口不能 ...
- VIM配置相关记录
把一直使用中的vim配置做个GIT入库管理,也把之前积累在机器上的文档,做个汇总. https://github.com/wujuguang/kyvim 1. 安装完整版vim vi和vim的区别?在 ...
- Ink – 帮助你快速创建响应式邮件(Email)的框架
Ink 可以帮助你快速创建响应的 HTML 电子邮件,可工作在任何设备和客户端.这个 CSS 框架帮助您构建可在任何设备上阅读的 HTML 电子邮件.曾经需要你兼顾各种邮件客户端的日子一去不复返了,I ...
- Node.js 入门手册:那些最流行的 Web 开发框架
这篇文章与大家分享最流行的 Node.js Web 开发框架.Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编 ...
- IIS负载均衡的NLB解决方案
在现行的许多网络应用中,有时一台服务器往往不能满足客户端的要求,此时只能通过增加服务器来解决问题.那么,有没有一个办法或者技术来解决此类问题呢?使用Windows 2000或Windows Serve ...
- dp - 2015 Multi-University Training Contest 2 1004 Delicious Apples
Delicious Apples Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=5303 Mean: 一条长为L的环形路上种着n棵 ...
- Ubuntu搭建Android交叉编译环境
一.下载 Android NDK Android NDK官方下载页:http://developer.android.com/tools/sdk/ndk/index.html如果需要旧版本的,比如10 ...
- Winform开发框架之通用数据导入导出操作的事务性操作完善
1.通用数据导入导出操作模块回顾 在我的Winfrom开发框架里面,有一个通用的导入模块,它在默默处理这把规范的Excel数据导入到不同的对象表里面,一直用它来快速完成数据导入的工作.很早在随笔< ...