多线程03_线程死锁

死锁

两个线程对两个同步对象具有循环依赖时,就会发生死锁。即同步嵌套同步,而锁却不同。
线程饿死和活锁虽然不像死锁一样是常见的问题,但是对于并发编程的设计者来说就像一次邂逅一样。当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形:

  • 当所有线程在程序中执行 Object.wait (0),参数为 0 的 wait 方法。
  • 程序将发生活锁直到在相应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()。
  • 当所有线程卡在无限循环中。

停止线程

  • stop方法已经过时,如何停止线程?
    停止线程的方法只有一种,就是run方法结束。如何让run方法结束呢?开启多线程运行,运行代码通常是循环体,只要控制住循环,就可以让run方法结束,也就是结束线程。
    特殊情况:当线程属于冻结状态,就不会读取循环控制标记,则线程就不会结束。为解决该特殊情况,可引入Thread类中的Interrupt方法结束线程的冻结状态;当没有指定的方式让冻结线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态。
  • interrupt方法
    中断线程,中断状态将被清除,它还将收到一个 InterruptedException。

ThreadLocal类

ThreadLocal类提供了线程局部 (thread-local) 变量。是一个线程级别的局部变量,并非“本地线程”。
ThreadLocal为每个使用该变量的线程,提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本。
下面是线程局部变量(ThreadLocal variables)的关键点:

  • 一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。
  • ThreadLocal 实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。
  • 当多个线程访问 ThreadLocal 实例时,每个线程维护 ThreadLocal 提供的独立的变量副本。
  • 常用的使用可在 DAO 模式中见到,当 DAO 类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响(基于线程的单例)。

几个问题

  • 什么时候抛出InvalidMonitorStateException异常?为什么?
    调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出 IllegalMonitorStateException 的异常。
    也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用 wait ()/notify ()/notifyAll ()时。由于该异常是 RuntimeExcpetion 的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意。
    作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。
  • 在静态方法上使用同步时会发生什么事?
    同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。
    它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
  • 当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?
    可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
  • 在一个对象上两个线程可以调用两个不同的同步实例方法吗?
    不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。

简单代码测试

测试三

posted @ 2018-10-04 09:48  bkycrab  阅读(113)  评论(0编辑  收藏  举报