Java多线程学习笔记 - 九、Java多线程中的死锁

1、java对象的锁

        java中的每个对象都有一个唯一的锁。在任何时刻,最多只允许一个线程拥有这把锁。当我们使用synchronized关键字时,锁的概念就会出现。 

2、死锁示例

        下面是容易出现死锁的示例参考代码,不建议使用在线 IDE 运行上述程序。建议使用javac命令编译之后使用java命令运行。

// Java program to illustrate Deadlock
// in multithreading.
class Util
{
	// Util class to sleep a thread
	static void sleep(long millis)
	{
		try
		{
			Thread.sleep(millis);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
}

// This class is shared by both threads
class Shared
{
	// first synchronized method
	synchronized void test1(Shared s2)
	{
		System.out.println("test1-begin");
		Util.sleep(1000);

		// taking object lock of s2 enters
		// into test2 method
		s2.test2();
		System.out.println("test1-end");
	}

	// second synchronized method
	synchronized void test2()
	{
		System.out.println("test2-begin");
		Util.sleep(1000);
		// taking object lock of s1 enters
		// into test1 method
		System.out.println("test2-end");
	}
}


class Thread1 extends Thread
{
	private Shared s1;
	private Shared s2;

	// constructor to initialize fields
	public Thread1(Shared s1, Shared s2)
	{
		this.s1 = s1;
		this.s2 = s2;
	}

	// run method to start a thread
	@Override
	public void run()
	{
		// taking object lock of s1 enters
		// into test1 method
		s1.test1(s2);
	}
}


class Thread2 extends Thread
{
	private Shared s1;
	private Shared s2;

	// constructor to initialize fields
	public Thread2(Shared s1, Shared s2)
	{
		this.s1 = s1;
		this.s2 = s2;
	}

	// run method to start a thread
	@Override
	public void run()
	{
		// taking object lock of s2
		// enters into test2 method
		s2.test1(s1);
	}
}


public class Deadlock
{
	public static void main(String[] args)
	{
		// creating one object
		Shared s1 = new Shared();

		// creating second object
		Shared s2 = new Shared();

		// creating first thread and starting it
		Thread1 t1 = new Thread1(s1, s2);
		t1.start();

		// creating second thread and starting it
		Thread2 t2 = new Thread2(s1, s2);
		t2.start();

		// sleeping main thread
		Util.sleep(2000);
	}
}

        上面代码进行了那些操作?

  1. 线程 t1 启动并通过获取 s1 的对象锁调用 test1 方法。
  2. 线程 t2 启动并通过获取 s2 的对象锁调用 test1 方法。
  3. t1 打印 test1-begin 和 t2 打印 test-2 begin 并且都等待 1 秒,因此如果其中任何一个线程都没有启动,那么两个线程都可以启动。
  4. t1 尝试获取 s2 的对象锁并调用 test2 方法,但由于它已经被 t2 获取,所以它一直等到它变得空闲。在获得 s2 的锁之前,它不会释放 s1 的锁。
  5. t2 也是如此。它试图获取 s1 的对象锁并调用方法 test1 但它已经被 t1 获取,所以它必须等到 t1 释放锁。t2 在获得 s1 的锁定之前也不会释放 s2 的锁定。
  6. 现在,两个线程都处于等待状态,等待对方释放锁。现在有一个竞争条件,即谁将首先释放锁。
  7. 由于它们都没有准备好释放锁,所以这是死锁条件。
  8. 当你运行这个程序时,它看起来就像执行被暂停了。

3、查看死锁

        在运行上一小节的代码之后,出现死锁时可以进入windows详细信息,可以看到两个java线程在运行。

         我们可以再启动一个cmd窗口,运行jcmd $PID Thread.print命令其中$PID是上图中的线程id。

        我这里输入jcmd 53364 Thread.print之后回车,会看到输出明确说明发现一个死锁。

 4、避免死锁

        避免嵌套锁:这是死锁的主要原因。死锁主要发生在我们给多个线程加锁时。如果我们已经给了一个线程,请避免给多个线程加锁。

        避免不必要的锁:我们应该只锁定那些需要的成员。不必要的锁定会导致死锁。

        使用join:当一个线程正在等待另一个线程完成时出现死锁情况。如果发生这种情况,我们可以使用 Thread.join 以及您认为执行所需的最长时间。

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/123599730