欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-27 20:12:20
死锁在多线程的情况下,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用synchronized,一种是用lock显式锁实现。而如果不恰当的使用了锁,且出现同时要锁多...

死锁在多线程的情况下,会出现数据不同步情况, 而为了避免这种情况,之前也说了:界区实现方法有两种,一种是用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再次尝试时会成功。

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

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