基于Java信号量解决死锁过程解析

死锁在多线程的情况下,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。

而如果不恰当的使用了锁,且出现同时要锁多个对象时,会出现死锁情况,如下:

package lockTest;
import java.util.Date;
/**
 * 崔素强
 * @author cuisuqiang@163.com
 */
public class LockTest {
	public static String obj1 = "obj1";
	public static String obj2 = "obj2";
	public static void main(String[] args) {
		LockA la = new LockA();
		new Thread(la).start();
		LockB lb = new LockB();
		new Thread(lb).start();
	}
}
class LockA implements Runnable{
	public void run() {
		try {
			System.out.println(new Date().toString() + " LockA 开始执行");
			while(true){
				synchronized (LockTest.obj1) {
					System.out.println(new Date().toString() + " LockA 锁住 obj1");
					Thread.sleep(3000); // 此处等待是给B能锁住机会
					synchronized (LockTest.obj2) {
						System.out.println(new Date().toString() + " LockA 锁住 obj2");
						Thread.sleep(60 * 1000); // 为测试,占用了就不放
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
class LockB implements Runnable{
	public void run() {
		try {
			System.out.println(new Date().toString() + " LockB 开始执行");
			while(true){
				synchronized (LockTest.obj2) {
					System.out.println(new Date().toString() + " LockB 锁住 obj2");
					Thread.sleep(3000); // 此处等待是给A能锁住机会
					synchronized (LockTest.obj1) {
						System.out.println(new Date().toString() + " LockB 锁住 obj1");
						Thread.sleep(60 * 1000); // 为测试,占用了就不放
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

看打印:

Mon Mar 31 10:52:38 CST 2014 LockA 开始执行
Mon Mar 31 10:52:38 CST 2014 LockA 锁住 obj1
Mon Mar 31 10:52:38 CST 2014 LockB 开始执行
Mon Mar 31 10:52:38 CST 2014 LockB 锁住 obj2

A锁住了B需要的,B锁住了A需要的,此时死锁产生。

为了解决这个问题,我们不使用显示的去锁

信号量可以控制资源能被多少线程访问,这里我们指定只能被一个线程访问,就做到了类似锁住。而信号量可以指定去获取的超时时间,我们可以根据这个超时时间,去做一个额外处理。

对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。

来看下如下代码:

package lockTest;
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
 * 崔素强
 * @author cuisuqiang@163.com
 */
public class UnLockTest {
	public static String obj1 = "obj1";
	public static final Semaphore a1 = new Semaphore(1);
	public static String obj2 = "obj2";
	public static final Semaphore a2 = new Semaphore(1);

	public static void main(String[] args) {
		LockAa la = new LockAa();
		new Thread(la).start();
		LockBb lb = new LockBb();
		new Thread(lb).start();
	}
}
class LockAa implements Runnable {
	public void run() {
		try {
			System.out.println(new Date().toString() + " LockA 开始执行");
			while (true) {
				if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
					System.out.println(new Date().toString() + " LockA 锁住 obj1");
					if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
						System.out.println(new Date().toString() + " LockA 锁住 obj2");
						Thread.sleep(60 * 1000); // do something
					}else{
						System.out.println(new Date().toString() + "LockA 锁 obj2 失败");
					}
				}else{
					System.out.println(new Date().toString() + "LockA 锁 obj1 失败");
				}
				UnLockTest.a1.release(); // 释放
				UnLockTest.a2.release();
				Thread.sleep(1000); // 马上进行尝试,现实情况下do something是不确定的
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
class LockBb implements Runnable {
	public void run() {
		try {
			System.out.println(new Date().toString() + " LockB 开始执行");
			while (true) {
				if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
					System.out.println(new Date().toString() + " LockB 锁住 obj2");
					if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
						System.out.println(new Date().toString() + " LockB 锁住 obj1");
						Thread.sleep(60 * 1000); // do something
					}else{
						System.out.println(new Date().toString() + "LockB 锁 obj1 失败");
					}
				}else{
					System.out.println(new Date().toString() + "LockB 锁 obj2 失败");
				}
				UnLockTest.a1.release(); // 释放
				UnLockTest.a2.release();
				Thread.sleep(10 * 1000); // 这里只是为了演示,所以tryAcquire只用1秒,而且B要给A让出能执行的时间,否则两个永远是死锁
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

看打印情况:

Mon Mar 31 10:57:07 CST 2014 LockA 开始执行
Mon Mar 31 10:57:07 CST 2014 LockB 开始执行
Mon Mar 31 10:57:07 CST 2014 LockB 锁住 obj2
Mon Mar 31 10:57:07 CST 2014 LockA 锁住 obj1
Mon Mar 31 10:57:08 CST 2014LockB 锁 obj1 失败
Mon Mar 31 10:57:08 CST 2014LockA 锁 obj2 失败
Mon Mar 31 10:57:09 CST 2014 LockA 锁住 obj1
Mon Mar 31 10:57:09 CST 2014 LockA 锁住 obj2

第一次两个线程获取信号量时都会失败,因为失败后B等待时间长,所以A再次尝试时会成功。

实际中,你执行任务内容不同,所需时间是不同的。另外不同的线程,对于获取信号量失败的处理也可能是不同的。所以,虽然不会产生死锁,但是你要根据实际情况,来编写获取失败后的处理机制。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2020-10-16

Java线程死锁实例及解决方法

这篇文章主要介绍了Java线程死锁实例及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.死锁的定义 所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进 2.死锁产生的必要条件 互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个线程所占有.此时若有线程请求该资源,则请求线程只能等待. 不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程倾向夺

java线程死锁代码示例

死锁是操作系统层面的一个错误,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一. 事实上,计算机世界有很多事情需要多线程方式去解决,因为这样才能最大程度上利用资源,才能体现出计算的高效.但是,实际上来说,计算机系统中有很多一次只能由一个进程使用的资源的情况,例如打印机,同时只能有一个进程控制它.在多通道程序设计环境中,若干进程往往要共享这类资源,而且一个进程所需要的资源还很有可能不止一个.因此,就会

Java中常见死锁与活锁的实例详解

本文介绍了Java中常见死锁与活锁的实例详解,分享给大家,具体如下: 顺序死锁:过度加锁,导致由于执行顺序的原因,互相持有对方正在等待的锁 资源死锁:多个线程在相同的资源上发生等待 由于调用顺序而产生的死锁 public class Test { Object leftLock = new Object(); Object rightLock = new Object(); public static void main(String[] args) { final Test test = ne

Java模拟死锁发生之演绎哲学家进餐问题案例详解

本文实例讲述了Java模拟死锁发生之演绎哲学家进餐问题.分享给大家供大家参考,具体如下: 一 点睛 常见的死锁形式:当线程1已经占据资源R1,并持有资源R1上的锁,而且还在等待资源R2的锁:而线程2已经占据资源R2,并且持有资源R2上的锁,却正在等待资源R1上的锁.如果两个线程不释放自己占据的资源锁,而且还申请对方资源上的锁,申请不到时只能等待,而且它们只能永远的等待下去. 二 实战 1 代码 public class DeadLockDemo { /** knife锁 */ private s

java中常见的死锁以及解决方法代码

在java中我们常常使用加锁机制来确保线程安全,但是如果过度使用加锁,则可能导致锁顺序死锁.同样,我们使用线程池和信号量来限制对资源的使用,但是这些被限制的行为可能会导致资源死锁.java应用程序无法从死锁中恢复过来,因此设计时一定要排序那些可能导致死锁出现的条件. 1.一个最简单的死锁案例 当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞.在线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么这两个线程将永远地等待下去.这种就是最简答的死锁形式(

java实现死锁的示例代码

什么是死锁 我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行.如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进:对于B车来说,它走过桥面右边的一段路(即占有了桥的一部分资源),要想过桥还须等待A车让出左边的桥面,此时B车也不能前进.两边的车都不倒车,结果造成互相等待对方让出桥面,但是谁也不让路,就会无休止地等下去.这种现象就是死锁

史上最通俗理解的Java死锁代码演示

死锁的概念 知识储备 对象锁:Java一切皆对象,每个类都有一个class文件.由class文件可以new出对象,我们简单认识 下java对象,对象有个对象头信息,也就是这个对象概述,其中一条信息就是对象锁,也就是我们当前对象有没有被锁定,被哪个引用锁定. synchronized:synchronized是java关键词,如果运用到方法上代表我们锁的是这个方法,如果我们锁的代码块,代表再这个代码块内我们持有这个锁,Java Effective也是提倡减小锁的范围.我们进入同步代码块会加锁,执行

史上最难的一道Java面试题

题目如下: public class TestSync2 implements Runnable { int b = 100; synchronized void m1() throws InterruptedException { b = 1000; Thread.sleep(500); //6 System.out.println("b=" + b); } synchronized void m2() throws InterruptedException { Thread.sle

TF-IDF理解及其Java实现代码实例

TF-IDF 前言 前段时间,又具体看了自己以前整理的TF-IDF,这里把它发布在博客上,知识就是需要不断的重复的,否则就感觉生疏了. TF-IDF理解 TF-IDF(term frequency–inverse document frequency)是一种用于资讯检索与资讯探勘的常用加权技术, TFIDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类.TFIDF实际上是:TF * IDF,TF词频(T

史上最简单的MyBatis动态SQL入门示例代码

假如有如下的关于书籍基本信息的表: DROP DATABASE IF EXISTS `books`; CREATE DATABASE `books`; USE books; DROP TABLE IF EXISTS `book`; CREATE TABLE `book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(128) DEFAULT NULL, `author` varchar(64) DEFAULT NULL, `pres

Ext第一周 史上最强学习笔记---GridPanel(基础篇)

如果你想实现什么特效,这个文字不适合你,但如果你想Ext文章,我想鄙人的小文非常适合你. 另:这篇教程是建立在Ext2.2上的.这个很多教程都没说明.让人很糊涂.我在此特别说明 网上很多教程,但是实际使用并不是很多,我想作为一个星期一个星期的总结来给各位网络上的朋友带来帮助.希望各位有用.我会从每个细节都说明,尽量不漏掉作为初学者需要知道的基础知识.第一次写教程,见笑了. 因为我学这个是这样一个过程,通过一个控件了解其他的控件,剩下的就是属性查API了.API我自己在翻译CHM版的.因为是个人操

Java死锁_动力节点Java学院整理

死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源.在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它. Java死锁范例 ThreadDeadlock.java package com.bjpowernode.threads; public class ThreadDeadlock { public static void main(String[] args) throws InterruptedException {

2020史上最全IDEA插件总结(推荐收藏)

IDEA 插件安装 步骤 IDEA里面,选择打开 File --> Settings --> Plugins 在Plugins里面, 可以搜索需要的插件 (下面的标题),然后安装 如下图: AceJump 全栈必备,作为一个全能的程序员 ,用鼠标,太伤自尊了,他就可以帮到你 AceJump其实是一款能够代替鼠标的软件,只要安装了这款插件,可以在代码中跳转到任意位置.按快捷键进入 AceJump 模式后(默认是 Ctrl+J),再按任一个字符,插件就会在屏幕中这个字符的所有出现位置都打上标签,你

史上最全的PHP正则表达式(手机号需要加上177-***)

首先看下正则表达式思维导图: 一.校验数字的表达式  1 数字: ^[0-9]*$ 2 n位的数字: ^\d{n}$ 3 至少n位的数字: ^\d{n,}$ 4 m-n位的数字: ^\d{m,n}$ 5 零和非零开头的数字: ^(0|[1-9][0-9]*)$ 6 非零开头的最多带两位小数的数字: ^([1-9][0-9]*)+(.[0-9]{1,2})?$ 7 带1-2位小数的正数或负数: ^(\-)?\d+(\.\d{1,2})?$ 8 正数.负数.和小数: ^(\-|\+)?\d+(\.\

史上最详细的CocoaPods安装教程(图文)

虽然网上关于CocoaPods安装教程多不胜数,但是我在安装的过程中还是出现了很多错误,所以大家可以照下来步骤装一下,我相信会很好用. 前言 在iOS项目中使用第三方类库可以说是非常常见的事,但是要正确地配置他们有时候是非常繁琐的事情,幸运的是CocoaPods是一个很好的解决方案. 什么是CocoaPods CocoaPods是OS X和iOS下的一个第三类库管理工具,通过CocoaPods工具我们可以为项目添加被称为"Pods"的依赖库(这些类库必须是CocoaPods本身所支持的