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);
}
}
上面代码进行了那些操作?
- 线程 t1 启动并通过获取 s1 的对象锁调用 test1 方法。
- 线程 t2 启动并通过获取 s2 的对象锁调用 test1 方法。
- t1 打印 test1-begin 和 t2 打印 test-2 begin 并且都等待 1 秒,因此如果其中任何一个线程都没有启动,那么两个线程都可以启动。
- t1 尝试获取 s2 的对象锁并调用 test2 方法,但由于它已经被 t2 获取,所以它一直等到它变得空闲。在获得 s2 的锁之前,它不会释放 s1 的锁。
- t2 也是如此。它试图获取 s1 的对象锁并调用方法 test1 但它已经被 t1 获取,所以它必须等到 t1 释放锁。t2 在获得 s1 的锁定之前也不会释放 s2 的锁定。
- 现在,两个线程都处于等待状态,等待对方释放锁。现在有一个竞争条件,即谁将首先释放锁。
- 由于它们都没有准备好释放锁,所以这是死锁条件。
- 当你运行这个程序时,它看起来就像执行被暂停了。
3、查看死锁
在运行上一小节的代码之后,出现死锁时可以进入windows详细信息,可以看到两个java线程在运行。
我们可以再启动一个cmd窗口,运行jcmd $PID Thread.print命令,其中$PID是上图中的线程id。
我这里输入jcmd 53364 Thread.print之后回车,会看到输出明确说明发现一个死锁。
4、避免死锁
避免嵌套锁:这是死锁的主要原因。死锁主要发生在我们给多个线程加锁时。如果我们已经给了一个线程,请避免给多个线程加锁。
避免不必要的锁:我们应该只锁定那些需要的成员。不必要的锁定会导致死锁。
使用join:当一个线程正在等待另一个线程完成时出现死锁情况。如果发生这种情况,我们可以使用 Thread.join 以及您认为执行所需的最长时间。