Java多线程-死锁的出现和解决方案

电脑杂谈  发布时间:2020-05-05 18:13:30  来源:网络整理

线程死锁 解决_避免线程死锁_多线程死锁的例子

Java多线程死锁

什么是僵局?

死锁是一种情况,其中多个线程同时被阻塞,并且其中一个或所有线程都在等待释放资源. 由于线程被无限期阻塞,因此程序无法正常运行. 如图所示: 一件宝物需要两个钥匙才能打开,两个人同时来了,每个人都有一把钥匙多线程死锁的例子,但是双方都在等待对方交出钥匙来打开宝物,而没有一个人释放了自己的钥匙. 这样,这两个人就陷入僵局,直到开发人员发现这种情况.

死锁的根本原因是不恰当地使用了“ synchronized”关键字来管理对特定对象的线程访问. “ synchronized”关键字的作用是确保一次只允许一个线程执行特定的代码. 因此,被允许执行的线程必须首先具有对变量或对象的独占访问权限. 当一个线程访问一个对象时,该线程将锁定该对象,并且此锁将导致也希望访问同一对象的其他线程被阻塞,直到第一个线程释放它施加在该对象上的锁为止.

避免线程死锁_多线程死锁的例子_线程死锁 解决

如果您不太了解同步多线程死锁的例子,请单击此处

例如

大多数死锁发生在您不知道的时候. 让我们举个例子看看什么是僵局.

1. 同步嵌套.

避免线程死锁_多线程死锁的例子_线程死锁 解决

Synchronized关键字可以确保当多个线程访问同步的修改后的方法时进行同步. 也就是说,当线程A访问此方法时,线程B也访问此方法,然后线程B将阻塞,等待线程A完成执行,然后才能访问它. 在这里,您需要使用synced持有的同步锁. 具体看一下代码:

//首先我们先定义两个final的对象锁.可以看做是共有的资源.
 final Object lockA = new Object();
 final Object lockB = new Object();
//生产者A
  class  ProductThreadA implements Runnable{
      @Override
      public void run() {
//这里一定要让线程睡一会儿来模拟处理数据 ,要不然的话死锁的现象不会那么的明显.这里就是同步语句块里面,首先获得对象锁lockA,然后执行一些代码,随后我们需要对象锁lockB去执行另外一些代码.
          synchronized (lockA){
          //这里一个log日志
              Log.e("CHAO","ThreadA lock  lockA");
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              synchronized (lockB){
               //这里一个log日志
                  Log.e("CHAO","ThreadA lock  lockB");
                  try {
                      Thread.sleep(2000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
  }
  //生产者B
  class  ProductThreadB implements Runnable{
  //我们生产的顺序真好好生产者A相反,我们首先需要对象锁lockB,然后需要对象锁lockA.
      @Override
      public void run() {
          synchronized (lockB){
           //这里一个log日志
              Log.e("CHAO","ThreadB lock  lockB");
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              synchronized (lockA){
               //这里一个log日志
                  Log.e("CHAO","ThreadB lock  lockA");
                  try {
                      Thread.sleep(2000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
  }
  //这里运行线程
ProductThreadA productThreadA = new ProductThreadA();
ProductThreadB productThreadB = new ProductThreadB();
      Thread threadA = new Thread(productThreadA);
      Thread threadB = new Thread(productThreadB);
      threadA.start();
      threadB.start();

分析,当线程A开始执行run方法时,它将首先保持对象锁localA,然后休眠2秒钟. 这时,threadB也开始执行run方法,该方法持有localB对象锁. 当threadA运行时,到第二种同步方法时,发现无法使用localB对象锁(threadB没有释放localB锁),threadA在这里停止以等待localB锁. 然后,线程B还执行第二种同步方法来访问localA对象锁. 当我发现本地A尚未释放(线程A没有释放localA锁)时,线程B也在此处停止以等待本地A锁被释放. 这样,两个线程都无法继续执行并进入死锁状态. 查看运行结果:

10-20 14:54:39.940 18162-18178/? E/CHAO: ThreadA lock  lockA
10-20 14:54:39.940 18162-18179/? E/CHAO: ThreadB lock  lockB

多线程死锁的例子_线程死锁 解决_避免线程死锁

没有死锁时,应打印四个日志,并且明显出现死锁现象.

僵局的原因

当我们了解在什么情况下会发生死锁以及什么是死锁时,我们应该尽力避免编写代码时的这种误解. 只要发生死锁,就必须同时满足以下四个条件. 如果没有建立任何条件,就不会发生死锁.

•互斥条件: 线程需要对分配的资源进行互斥控制,即,资源仅在一段时间内被进程占用. 如果此时其他进程请求资源,则请求进程只能等待.

避免线程死锁_线程死锁 解决_多线程死锁的例子

•无剥夺条件: 由该进程获得的资源在用完之前不能被其他进程强行带走,也就是说,只能由获得该资源的线程释放该资源(仅活动释放).

•请求和保持条件: 线程已保留至少一个资源,但发出了新的资源请求,并且该资源已被其他线程占用. 这时,请求线程被阻止,但保留了已获取的资源. 不要放开.

•循环等待条件: 存在一个循环等待的线程资源链,并且链中每个线程同时请求链中每个线程获得的资源.

解决方案陷入僵局

说实话,为避免死锁,编写自己的代码时必须注意. 在这里,我引用了其他人的解决方案,但我对这些解决方案的了解不多,我也不太能谈论具体示例.

java exchange 669823128


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-199878-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...