`
cuisuqiang
  • 浏览: 3935649 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
3feb66c0-2fb6-35ff-968a-5f5ec10ada43
Java研发技术指南
浏览量:3650303
社区版块
存档分类
最新评论

用Java信号量 解决死锁

    博客分类:
  • JDK
阅读更多

死锁在多线程的情况下,在竞争竞态条件与临界区(http://cuisuqiang.iteye.com/blog/2020152)出现时,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用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需要的,此时死锁产生。

 

为了解决这个问题,我们不使用显示的去锁,我们用信号量(http://cuisuqiang.iteye.com/blog/2020146)去控制。

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

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

来看下如下代码:

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再次尝试时会成功。

 

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

 

请您到ITEYE网站看 java小强 原创,谢谢!

http://cuisuqiang.iteye.com/ 

自建博客地址:http://www.javacui.com/ ,内容与ITEYE同步!

4
2
分享到:
评论

相关推荐

    DiningPhilosopherGUI:Java中GUI的进餐哲学家问题。 使用信号量解决死锁

    餐饮哲学家GUI Java中GUI的进餐哲学家问题。 使用信号量解决死锁。

    java多线程编程总结

    详细的讲述了多线程的各种用法 ...Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

    Java信号量Semaphore

    Semaphore  Semaphore分为单值和多值两种,前者只能被一个线程获得,...单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场

    Java多线程编程总结

    Java 线程系列博文总结word化,编目如下,欢迎互相...Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器

    java高并发相关知识点.docx

    并发控制:Java中的并发控制机制,包括信号量、原子变量、倒计时等。 线程安全:Java中的线程安全,包括同步方法和同步块等。 死锁:Java中的死锁,包括如何避免死锁和如何解除死锁。 性能优化:Java中的性能优化,...

    Java 线程总结

    Java线程:概念与原理 ...Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

    JavaMultiThreadingExamples:带有中文注释的基本Java多线程示例

    #Java多线程示例-Java多线程应用实例 1- Java多线程:启动线程 2- Java多线程:易失性–基本线程通信 3- Java多线程:同步 ...12- Java多线程:信号量 13- Java多线程:可调用和未来 14- Java多线程:中断线程 从分叉

    Java并发编程(学习笔记).xmind

    (3)使用信号量将任何一种容器变成有界阻塞容器 栅栏 能够阻塞一组线程直到某个事件发生 栅栏和闭锁的区别 所有线程必须同时到达栅栏位置,才能继续执行 闭锁用于等待事件,而栅栏...

    MultithreadingJava:来自Cave of Programming http的John Purcell的Java多线程课程代码

    课程内容: 1- Java 多线程:启动线程 2- Java 多线程:Volatile – ...12- Java 多线程:信号量 13- Java 多线程:Callable 和 Future 14- Java 多线程:中断线程 15- Java 多线程:Swing 中的多线程与 SwingWorker

    multithreading

    #Java多线程1- Java多线程:启动线程2- Java多线程:易失...Java多线程:低级生产者-消费者10- Java多线程:可重入锁11- Java多线程:死锁12- Java多线程:信号量13- Java多线程:可调用和未来14- Java多线程:中断线程

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例146 使用信号量实现线程同步 190 实例147 使用原子变量实现线程同步 191 实例148 使用事件分配线程更新Swing控件 193 实例149 使用SwingWorker类完成耗时操作 194 第7章 反射与异常处理 195 7.1 反射的基础 196 ...

    个人总结的深入java多线程开发

    6)信号量Semaphore 31 7)ReentrantLock可重入的互斥锁定 Lock 32 8)阻塞队列BlockingQueue 34 9)已完成任务队列CompletionService 36 10)计时器CountDownLatch 37 11)周期性同步工具CyclicBarrier 38 12)异步计算的...

    Java并发编程实战

    5.5.3 信号量82 5.5.4 栅栏83 5.6 构建高效且可伸缩的结果缓存85 第二部分 结构化并发应用程序 第6章 任务执行93 6.1 在线程中执行任务93 6.1.1 串行地执行任务94 6.1.2 显式地为任务创建线程94 6.1.3 ...

Global site tag (gtag.js) - Google Analytics