这篇文章介绍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-死锁与线程间通信的更多相关文章

  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. Google Chrome 扩展程序开发

    根据公司的规定,每月八小时,弹性工作制.所以大家平时来的不太准时,如果有事,下班也就早些回去了.所以一个月下来工作时间可能不够,但是公司的考勤日历是这样的: 除了请假和法定节假日外,其他样式显示都是一 ...

  2. 《Programming with Objective-C》第三章 Working with Objects

    Object和普通变量的区别 If you’re used to using terms like the stack and the heap, a local variable is alloca ...

  3. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  4. Errors running builder 'JavaScript Validator' on project

    builders去掉JavaScript Validator. 如图: 参考文献: [1]http://www.cnblogs.com/enshrineZither/p/4062248.html

  5. [linux]收集一些好玩的命令

    1.rev命令 反转输出,输入的字符串. 在终端中输入:rev 输入需要字符串(支持中文) 2.asciiview命令 安装aview:apt-get install aview 再安装imagema ...

  6. 前端翻译:Activating Browser Modes with Doctype

    一.前言 原本备份: http://www.cnblogs.com/fsjohnhuang/p/3830623.html 由于本人英语能力有限,译本内容难免有误,望各位指正! 本译文不含附录部分,请知 ...

  7. 关于Latch争用

    Latch是什么     Latch是SQL Server引擎保证内存中的结构的一致性的轻量同步机制.比如索引,数据页和内部结构(比如非叶级索引页).SQL Server使用Buffer Latch保 ...

  8. Mysql的“Limit”操作

    Limit操作: ,; #返回第6-15行数据 ; #返回前5行 ,; #返回前5行 性能优化: 基于MySQL5.0中limit的高性能,我对数据分页也重新有了新的认识.测试SQL语句1: Sele ...

  9. 用UltraISO制作支持windows 7的U盘启动盘

    用UltraISO制作U盘启动盘,有人写过,我也看过,不过依照网上的那些文章,成功的并不多,经过几次试验,在不同的主板环境下成功概率高的方法应该如下:   1. UltraISO建议9.3以上 2. ...

  10. PHPWind 8.7中代码结构与程序执行顺序

    pw9在此不谈,他是完全重构的作品,是完全MVC下的体系.当然,其中很多东西在PW8.7下已经可见端倪. 主要代码结构 1. 以现代的观点,PW是多入口应用模式,程序根目录下的文件几乎都是入口: 2. ...