博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程系列---“基础篇”04之 synchronized关键字
阅读量:4311 次
发布时间:2019-06-06

本文共 21448 字,大约阅读时间需要 71 分钟。

转自:https://www.cnblogs.com/skywang12345/p/3479202.html  & https://home.cnblogs.com/u/swiftma(含部分修改)

概要

本章,会对synchronized关键字进行介绍。涉及到的内容包括:

  • synchronized原理
  • synchronized基本规则
  • synchronized方法 和 synchronized代码块
  • 实例锁 和 全局锁
  • 进一步了解synchronized

一.  synchronized原理

在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。

当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。

二. synchronized基本规则

我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。

  • 第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
  • 第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块
  • 第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

1. 第一条

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

下面是“synchronized代码块”对应的演示程序。

class MyRunable implements Runnable {        @Override    public void run() {        synchronized(this) { //mynote:这个锁实际上就是MyRunnable对象本身            try {                  for (int i = 0; i < 5; i++) {                    Thread.sleep(100); // 休眠100ms                    System.out.println(Thread.currentThread().getName() + " loop " + i);                  }            } catch (InterruptedException ie) {              }        }      }}public class Demo1_1 {    public static void main(String[] args) {          Runnable demo = new MyRunable();     // 新建“Runnable对象”        Thread t1 = new Thread(demo, "t1");  // 新建“线程t1”, t1是基于demo这个Runnable对象        Thread t2 = new Thread(demo, "t2");  // 新建“线程t2”, t2是基于demo这个Runnable对象        t1.start();                          // 启动“线程t1”        t2.start();                          // 启动“线程t2”     } } //MYnote:因为t1和t2启动的时候都会用到demo这个共同的对象,而这个对象又加了锁,因此它们的run方法是互斥的。

运行结果:(其中的一种,因为究竟是t1先执行完,还是t2先执行完,是不确定的)

t1 loop 0t1 loop 1t1 loop 2t1 loop 3t1 loop 4t2 loop 0t2 loop 1t2 loop 2t2 loop 3t2 loop 4

结果说明

run()方法中存在“synchronized(this)代码块”,而且t1和t2都是基于"demo这个Runnable对象"创建的线程。这就意味着,我们可以将synchronized(this)中的this看作是“demo这个Runnable对象”;因此,线程t1和t2共享“demo对象的同步锁”。所以,当一个线程运行的时候,另外一个线程必须等待“运行线程”释放“demo的同步锁”之后才能运行。

如果你确认,你搞清楚这个问题了。那我们将上面的代码进行修改,然后再运行看看结果怎么样,看看你是否会迷糊。修改后的源码如下:

class MyThread extends Thread {        public MyThread(String name) {        super(name);    }    @Override    public void run() {        synchronized(this) {            try {                  for (int i = 0; i < 5; i++) {                    Thread.sleep(100); // 休眠100ms  (如果不加sleep,就会看到非常明显得t1和t2抢着先后顺序打印)                    System.out.println(Thread.currentThread().getName() + " loop " + i);                  }            } catch (InterruptedException ie) {              }        }      }}public class Demo1_2 {    public static void main(String[] args) {          Thread t1 = new MyThread("t1");  // 新建“线程t1”        Thread t2 = new MyThread("t2");  // 新建“线程t2”        t1.start();                          // 启动“线程t1”        t2.start();                          // 启动“线程t2”     } }

代码说明

比较Demo1_2 和 Demo1_1,我们发现,Demo1_2中的MyThread类是直接继承于Thread,而且t1和t2都是MyThread子线程。
幸运的是,在“Demo1_2的run()方法”也调用了synchronized(this),正如“Demo1_1的run()方法”也调用了synchronized(this)一样!
那么,Demo1_2的执行流程是不是和Demo1_1一样呢?
运行结果:

t1 loop 0t2 loop 0t1 loop 1t2 loop 1t1 loop 2t2 loop 2t1 loop 3t2 loop 3t1 loop 4t2 loop 4 (mynote:假设没有加入休眠,就会出现两个线程竞争打印输出,无序,交叉执行。这个例子是两个对象,说明this代表了两种不同的锁,因此不会出现阻塞,交叉这执行run)
 

结果说明

如果这个结果一点也不令你感到惊讶,那么我相信你对synchronized和this的认识已经比较深刻了。否则的话,请继续阅读这里的分析。
synchronized(this)中的this是指“当前的类对象”,即synchronized(this)所在的类对应的当前对象。它的作用是获取“当前对象的同步锁”。
对于Demo1_2中,synchronized(this)中的this代表的是MyThread对象,而t1和t2是两个不同的MyThread对象,因此t1和t2在执行synchronized(this)时,获取的是不同对象的同步锁。对于Demo1_1对而言,synchronized(this)中的this代表的是MyRunable对象;t1和t2共同一个MyRunable对象,因此,一个线程获取了对象的同步锁,会造成另外一个线程等待。

 2. 第二条

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。

下面是“synchronized代码块”对应的演示程序。

class Count {    // 含有synchronized同步块的方法    public void synMethod() {        synchronized(this) {            try {                  for (int i = 0; i < 5; i++) {                    Thread.sleep(100); // 休眠100ms                    System.out.println(Thread.currentThread().getName() + " synMethod loop " + i);                  }            } catch (InterruptedException ie) {              }        }      }    // 非同步的方法    public void nonSynMethod() {        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100);                System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i);              }        } catch (InterruptedException ie) {          }    }}public class Demo2 {    public static void main(String[] args) {          final Count count = new Count();        // 新建t1, t1会调用“count对象”的synMethod()方法        Thread t1 = new Thread(                new Runnable() {                    @Override                    public void run() {                        count.synMethod();                    }                }, "t1");        // 新建t2, t2会调用“count对象”的nonSynMethod()方法        Thread t2 = new Thread(                new Runnable() {                    @Override                    public void run() {                        count.nonSynMethod();                    }                }, "t2");          t1.start();  // 启动t1        t2.start();  // 启动t2    } }

运行结果

t1 synMethod loop 0t2 nonSynMethod loop 0t1 synMethod loop 1t2 nonSynMethod loop 1t1 synMethod loop 2t2 nonSynMethod loop 2t1 synMethod loop 3t2 nonSynMethod loop 3t1 synMethod loop 4t2 nonSynMethod loop 4

结果说明

主线程中新建了两个子线程t1和t2。t1会调用count对象的synMethod()方法,该方法内含有同步块;而t2则会调用count对象的nonSynMethod()方法,该方法不是同步方法。t1运行时,虽然调用synchronized(this)获取“count的同步锁”;但是并没有造成t2的阻塞,因为t2没有用到“count”同步锁。

 3. 第三条

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

我们将上面的例子中的nonSynMethod()方法体的也用synchronized(this)修饰。修改后的源码如下:

class Count {    // 含有synchronized同步块的方法    public void synMethod() {        synchronized(this) {            try {                  for (int i = 0; i < 5; i++) {                    Thread.sleep(100); // 休眠100ms                    System.out.println(Thread.currentThread().getName() + " synMethod loop " + i);                  }            } catch (InterruptedException ie) {              }        }      }    // 也包含synchronized同步块的方法    public void nonSynMethod() {        synchronized(this) {            try {                  for (int i = 0; i < 5; i++) {                    Thread.sleep(100);                    System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i);                  }            } catch (InterruptedException ie) {              }        }    }}public class Demo3 {    public static void main(String[] args) {          final Count count = new Count();        // 新建t1, t1会调用“count对象”的synMethod()方法        Thread t1 = new Thread(                new Runnable() {                    @Override                    public void run() {                        count.synMethod();                    }                }, "t1");        // 新建t2, t2会调用“count对象”的nonSynMethod()方法        Thread t2 = new Thread(                new Runnable() {                    @Override                    public void run() {                        count.nonSynMethod();                    }                }, "t2");          t1.start();  // 启动t1        t2.start();  // 启动t2    } }

运行结果

t1 synMethod loop 0t1 synMethod loop 1t1 synMethod loop 2t1 synMethod loop 3t1 synMethod loop 4t2 nonSynMethod loop 0t2 nonSynMethod loop 1t2 nonSynMethod loop 2t2 nonSynMethod loop 3t2 nonSynMethod loop 4

结果说明

主线程中新建了两个子线程t1和t2。t1和t2运行时都调用synchronized(this),这个this是Count对象(count),而t1和t2共用count。因此,在t1运行时,t2会被阻塞,等待t1运行释放“count对象的同步锁”,t2才能运行。

 三. synchronized方法 和 synchronized代码块

“synchronized方法”是用synchronized修饰方法,而 “synchronized代码块”则是用synchronized修饰代码块。

synchronized方法示例

public synchronized void foo1() {    System.out.println("synchronized methoed");}

synchronized代码块

public void foo2() {    synchronized (this) {
System.out.println("synchronized methoed"); }}

synchronized代码块中的this是指当前对象。也可以将this替换成其他对象,例如将this替换成obj,则foo2()在执行synchronized(obj)时就获取的是obj的同步锁。

synchronized代码块可以更精确的控制冲突限制访问区域,有时候表现更高效率。下面通过一个示例来演示:

// Demo4.java的源码public class Demo4 {    public synchronized void synMethod() {        for(int i=0; i<1000000; i++)            ;    }    public void synBlock() {        synchronized( this ) {            for(int i=0; i<1000000; i++)                ;        }    }    public static void main(String[] args) {        Demo4 demo = new Demo4();        long start, diff;        start = System.currentTimeMillis();                // 获取当前时间(millis)        demo.synMethod();                                // 调用“synchronized方法”        diff = System.currentTimeMillis() - start;        // 获取“时间差值”        System.out.println("synMethod() : "+ diff);                start = System.currentTimeMillis();                // 获取当前时间(millis)        demo.synBlock();                                // 调用“synchronized方法块”        diff = System.currentTimeMillis() - start;        // 获取“时间差值”        System.out.println("synBlock()  : "+ diff);    }}

 

(某一次)执行结果

synMethod() : 11synBlock() : 3

synchronized同步方法和同步代码块的区别

同步方法默认使用this或者当前类做为锁。

同步代码块可以选择以什么来加锁,比同步方法更精确,我们可以选择只有会在同步发生同步问题的代码加锁,而并不是整个方法。

同步方法使用synchronized修饰,而同步代码块使用synchronized(this){}修饰。

四. 实例锁 和 全局锁

实例锁 -- 锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。

               实例锁对应的就是synchronized关键字。
全局锁 -- 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。
               全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。

关于“实例锁”和“全局锁”有一个很形象的例子:

pulbic class Something {    public synchronized void isSyncA(){}    public synchronized void isSyncB(){}    public static synchronized void cSyncA(){}    public static synchronized void cSyncB(){}}

假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。

(01) x.isSyncA()与x.isSyncB() 
(02) x.isSyncA()与y.isSyncA()
(03) x.cSyncA()与y.cSyncB()
(04) x.isSyncA()与Something.cSyncA()

(01) 不能被同时访问。因为isSyncA()和isSyncB()都是访问同一个对象(对象x)的同步锁!

// LockTest1.java的源码class Something {    public synchronized void isSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncA");            }        }catch (InterruptedException ie) {          }      }    public synchronized void isSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncB");            }        }catch (InterruptedException ie) {          }      }}public class LockTest1 {    Something x = new Something();    Something y = new Something();    // 比较(01) x.isSyncA()与x.isSyncB()     private void test1() {        // 新建t11, t11会调用 x.isSyncA()        Thread t11 = new Thread(                new Runnable() {                    @Override                    public void run() {                        x.isSyncA();                    }                }, "t11");        // 新建t12, t12会调用 x.isSyncB()        Thread t12 = new Thread(                new Runnable() {                    @Override                    public void run() {                        x.isSyncB();                    }                }, "t12");          t11.start();  // 启动t11        t12.start();  // 启动t12    }    public static void main(String[] args) {        LockTest1 demo = new LockTest1();        demo.test1();    }}

 

运行结果

t11 : isSyncAt11 : isSyncAt11 : isSyncAt11 : isSyncAt11 : isSyncAt12 : isSyncBt12 : isSyncBt12 : isSyncBt12 : isSyncBt12 : isSyncB

 

(02) 可以同时被访问。因为访问的不是同一个对象的同步锁,x.isSyncA()访问的是x的同步锁,而y.isSyncA()访问的是y的同步锁。

// LockTest2.java的源码class Something {    public synchronized void isSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncA");            }        }catch (InterruptedException ie) {          }      }    public synchronized void isSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncB");            }        }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncA");            }         }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncB");            }         }catch (InterruptedException ie) {          }      }}public class LockTest2 {    Something x = new Something();    Something y = new Something();    // 比较(02) x.isSyncA()与y.isSyncA()    private void test2() {        // 新建t21, t21会调用 x.isSyncA()        Thread t21 = new Thread(                new Runnable() {                    @Override                    public void run() {                        x.isSyncA();                    }                }, "t21");        // 新建t22, t22会调用 x.isSyncB()        Thread t22 = new Thread(                new Runnable() {                    @Override                    public void run() {                        y.isSyncA();                    }                }, "t22");          t21.start();  // 启动t21        t22.start();  // 启动t22    }    public static void main(String[] args) {        LockTest2 demo = new LockTest2();        demo.test2();    }}

 

运行结果

t21 : isSyncAt22 : isSyncAt21 : isSyncAt22 : isSyncAt21 : isSyncAt22 : isSyncAt21 : isSyncAt22 : isSyncAt21 : isSyncAt22 : isSyncA

 

(03) 不能被同时访问。因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。

// LockTest3.java的源码class Something {    public synchronized void isSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncA");            }        }catch (InterruptedException ie) {          }      }    public synchronized void isSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncB");            }        }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncA");            }         }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncB");            }         }catch (InterruptedException ie) {          }      }}public class LockTest3 {    Something x = new Something();    Something y = new Something();    // 比较(03) x.cSyncA()与y.cSyncB()    private void test3() {        // 新建t31, t31会调用 x.isSyncA()        Thread t31 = new Thread(                new Runnable() {                    @Override                    public void run() {                        x.cSyncA();                    }                }, "t31");        // 新建t32, t32会调用 x.isSyncB()        Thread t32 = new Thread(                new Runnable() {                    @Override                    public void run() {                        y.cSyncB();                    }                }, "t32");          t31.start();  // 启动t31        t32.start();  // 启动t32    }    public static void main(String[] args) {        LockTest3 demo = new LockTest3();        demo.test3();    }}

 

运行结果

t31 : cSyncAt31 : cSyncAt31 : cSyncAt31 : cSyncAt31 : cSyncAt32 : cSyncBt32 : cSyncBt32 : cSyncBt32 : cSyncBt32 : cSyncB

 

(04) 可以被同时访问。因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。

// LockTest4.java的源码class Something {    public synchronized void isSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncA");            }        }catch (InterruptedException ie) {          }      }    public synchronized void isSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : isSyncB");            }        }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncA(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncA");            }         }catch (InterruptedException ie) {          }      }    public static synchronized void cSyncB(){        try {              for (int i = 0; i < 5; i++) {                Thread.sleep(100); // 休眠100ms                System.out.println(Thread.currentThread().getName()+" : cSyncB");            }         }catch (InterruptedException ie) {          }      }}public class LockTest4 {    Something x = new Something();    Something y = new Something();    // 比较(04) x.isSyncA()与Something.cSyncA()    private void test4() {        // 新建t41, t41会调用 x.isSyncA()        Thread t41 = new Thread(                new Runnable() {                    @Override                    public void run() {                        x.isSyncA();                    }                }, "t41");        // 新建t42, t42会调用 x.isSyncB()        Thread t42 = new Thread(                new Runnable() {                    @Override                    public void run() {                        Something.cSyncA();                    }                }, "t42");          t41.start();  // 启动t41        t42.start();  // 启动t42    }    public static void main(String[] args) {        LockTest4 demo = new LockTest4();        demo.test4();    }}

 

运行结果

t41 : isSyncAt42 : cSyncAt41 : isSyncAt42 : cSyncAt41 : isSyncAt42 : cSyncAt41 : isSyncAt42 : cSyncAt41 : isSyncAt42 : cSyncA

五. 进一步了解synchronized

介绍了synchronized的基本用法和原理,我们再从下面几个角度来进一步理解一下synchronized:

  • 可重入性
  • 内存可见性
  • 死锁

可重入性

synchronized有一个重要的特征,它是可重入的,也就是说,对同一个执行线程,它在获得了锁之后,在调用其他需要同样锁的代码时,可以直接调用,比如说,在一个synchronized实例方法内,可以直接调用其他synchronized实例方法。可重入是一个非常自然的属性,应该是很容易理解的,之所以强调,是因为并不是所有锁都是可重入的(后续章节介绍)。

可重入是通过记录锁的持有线程和持有数量来实现的,当调用被synchronized保护的代码时,检查对象是否已被锁,如果是,再检查是否被当前线程锁定,如果是,增加持有数量,如果不是被当前线程锁定,才加入等待队列,当释放锁时,减少持有数量,当数量变为0时才释放整个锁。

内存可见性

对于复杂一些的操作,synchronized可以实现原子操作,避免出现竞态条件,但对于明显的本来就是原子的操作方法,也需要加synchronized吗?比如说,对于下面的开关类Switcher,它只有一个boolean变量on和对应的setter/getter方法:

public class Switcher {    private boolean on;    public boolean isOn() {        return on;    }    public void setOn(boolean on) {        this.on = on;    }}

当多线程同时访问同一个Switcher对象时,会有问题吗?没有竞态条件问题,但正如上节所说,有内存可见性问题,而加上synchronized可以解决这个问题。

synchronized除了保证原子操作外,它还有一个重要的作用,就是保证内存可见性,在释放锁时,所有写入都会写回内存,而获得锁后,都会从内存中读最新数据。

不过,如果只是为了保证内存可见性,使用synchronzied的成本有点高,有一个更轻量级的方式,那就是给变量加修饰符volatile,如下所示:

public class Switcher {    private volatile boolean on;    public boolean isOn() {        return on;    }    public void setOn(boolean on) {        this.on = on;    }}

加了volatile之后,Java会在操作对应变量时插入特殊的指令,保证读写到内存最新值,而非缓存的值。

死锁

使用synchronized或者其他锁,要注意死锁,所谓死锁就是类似这种现象,比如, 有a, b两个线程,a持有锁A,在等待锁B,而b持有锁B,在等待锁A,a,b陷入了互相等待,最后谁都执行不下去。示例代码如下所示:

public class DeadLockDemo {    private static Object lockA = new Object();    private static Object lockB = new Object();    private static void startThreadA() {        Thread aThread = new Thread() {            @Override            public void run() {                synchronized (lockA) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                    }                    synchronized (lockB) {                    }                }            }        };        aThread.start();    }    private static void startThreadB() {        Thread bThread = new Thread() {            @Override            public void run() {                synchronized (lockB) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                    }                    synchronized (lockA) {                    }                }            }        };        bThread.start();    }    public static void main(String[] args) {        startThreadA();        startThreadB();    }}

运行后aThread和bThread陷入了相互等待。怎么解决呢?首先,应该尽量避免在持有一个锁的同时去申请另一个锁,如果确实需要多个锁,所有代码都应该按照相同的顺序去申请锁,比如,对于上面的例子,可以约定都先申请lockA,再申请lockB。

不过,在复杂的项目代码中,这种约定可能难以做到。还有一种方法是使用后续章节介绍的显式锁接口Lock,它支持尝试获取锁(tryLock)和带时间限制的获取锁方法,使用这些方法可以在获取不到锁的时候释放已经持有的锁,然后再次尝试获取锁或干脆放弃,以避免死锁。

如果还是出现了死锁,怎么办呢?Java不会主动处理,不过,借助一些工具,我们可以发现运行中的死锁,比如,Java自带的jstack命令会报告发现的死锁,对于上面的程序,在我的电脑上,jstack会有如下报告:

 

转载于:https://www.cnblogs.com/Hermioner/p/9839601.html

你可能感兴趣的文章
常用的20个强大的 Sublime Text 插件
查看>>
ajaxfileupload.js在IE中的支持问题
查看>>
tensorflow学习之(十)使用卷积神经网络(CNN)分类手写数字0-9
查看>>
当document.write里含有script标签时
查看>>
工作中常见问题
查看>>
JAVA 从一个List里删除包含另一个List的数据
查看>>
外国的月亮比较圆吗?外籍团队工作有感
查看>>
CentOS 关闭烦人的屏保
查看>>
分布式系统事务一致性解决方案
查看>>
ShuffleNet总结
查看>>
前后台验证字符串长度
查看>>
《算法导论 - 思考题》7-1 Hoare划分的正确性
查看>>
UVa 10491 奶牛和轿车(全概率公式)
查看>>
[Hadoop]-HDFS-架构篇
查看>>
Metronic-最优秀的基于Bootstrap的响应式网站模版
查看>>
20. Valid Parentheses
查看>>
IOS 简单的动画自定义方法(旋转、移动、闪烁等)
查看>>
js/jquery 实时监听输入框值变化的完美方案:oninput & onpropertychange
查看>>
axios
查看>>
PostgreSQL导出一张表到MySQL
查看>>