这篇文章介绍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 监视器方法(waitnotify 和 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-死锁与线程间通信的更多相关文章

  1. Java多线程学习(五)线程间通信知识点补充

    系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...

  2. Java多线程编程(三)线程间通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  3. C++多线程编程(三)线程间通信

    多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...

  4. Java多线程系列 基础篇02 线程的创建和运行

    1.线程创建的方式常用有两种 1. 继承 Thread 类创建线程 2. 实现 Runnable 接口创建线程 2.Thread 和 Runnable的区别 Thread和Runnable的相同点:都 ...

  5. 【JAVA并发第三篇】线程间通信

    线程间的通信 JVM在运行时会将自己管理的内存区域,划分为不同的数据区,称为运行时数据区.每个线程都有自己私有的内存空间,如下图示: Java线程按照自己虚拟机栈中的方法代码一步一步的执行下去,在这一 ...

  6. java常见面试题3:线程间通信

    写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z. 打印顺序为12A34B56C78D--5152Z.要求用线程间的通信. 代码清单: class Printer { private in ...

  7. Java多线程系列 基础篇03 线程的优先级和守护线程

    1. 线程优先级 现代操作系统中基本上使用时间分片的方式调度线程,通过设置线程优先级,使优先级高的线程获得时间片的次数多于优先级低的线程. 在java 线程中,通过一个整形变量prority来控制优先 ...

  8. Java多线程系列 基础篇01 线程的状态

    1.进程和线程 进程: 计算机中程序关于某数据集合的一次运行活动,是计算机系统进行资源分配和调度的基本单位,是操作系统结构的基础. 线程: 线程是进程的实例,是CPU进行资源分配和调度的最小单位,线程 ...

  9. (Java多线程系列五)守护线程

    守护线程 什么是守护线程 Java中有两种线程,一种是用户线程,一种是守护线程. 当进程不存在或主线程停止,守护线程也会自动停止. class DaemonThread extends Thread ...

随机推荐

  1. 跨域名 Cookie 传递测试

    问题 我打开了 www.aaa.com 里面的一个页面(www.aaa.com/hello.php),其中这个 hello.php 里面包含一个 <img> 标签,里面的 src 来自于 ...

  2. HTML注释引起的问题

    因为规范要求需要对页面进行说明,添加作者等信息,所以在cshtml的代码中添加了html注释,包括之前使用jsp也是这样做的: 在页面布局的时候,需要对高度进行动态计算,IE8以上没有问题,主要是在I ...

  3. 将PDM文件导出成CHM帮助文件

    实际开发中常常将维护数据库字段用 powerdesigner 维护起来,而实际要查阅 数据库用了什么字段是,常常又不方便去打开PDM 文件去找. 下面分享一个小工具,效果看下图: 有这个小工具, PD ...

  4. 网络通信分享(二):外网ip和内网ip

    一.内网ip包括两类: 1:tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下: 10.0.0.0/8:10.0.0.0-10.255.255.255  172.16.0.0/ ...

  5. C#ASP.NET 通用扩展函数之 LogicSugar 简单好用

    说明一下性能方面 还可以接受 循环1000次普通Switch是用了0.001秒 ,扩展函数为0.002秒  , 如果是大项目在有负载均衡的情况下完全可以无视掉,小项目也不会计较这点性能了. 注意需要引 ...

  6. 五分钟,运用cocoaui库,搭建主流iOS app中我的界面

    本项目基于天天团购项目,在上一篇中有说到! 首先介绍一些cocoaui,是国内的一名程序员做的开源的开源系统,目的是为了简化ios布局!官网地址:www.cocoaui.com,github地址:ht ...

  7. 开源一个基于nio的java网络程序

    因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程 ...

  8. 测试lua的效率

    这几天粗略的测试了一下lua的效率!首先声明这个测试很有针对性,大部分是针对游戏中的使用,而绝非lua的整体性效率(这个测试我不会),lua构建的上层逻辑中,大概使用的语句不太多,for,迭代,调用C ...

  9. Scrum 3.2 多鱼点餐系统开发进度(页面优化&下单详细信息页面)

    Scrum 3.2 多鱼点餐系统开发进度(页面优化&下单详细信息页面)  1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选 ...

  10. 【Win10】让 AppBarButton 支持更复杂的 Icon 内容

    最近有一个需求,需要制作这么一个 AppBarButton: 这个 AppBarButton 的 Icon 是一个评论框图标里面再显示评论数(大于 99 条则显示 99+).其中评论数是通过数据绑定得 ...