A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

     研究Java同步机制时,发现一个很容易被忽略的小问题,实际开发中非常容易遇见。而毕老师课程中没有做出真正性的提示(有间接提示)。所以写了日志后拿出来分享给大家:

  run方法类implements于Runnable还是extends自Thread在同步标记对象上有一点须要注意:
  如果run方法类extends自Thread那么每启动一个新的线程也就会产生一个新的run方法。如果启动两个线程,此时有两个run方法,虽然都是同一类中的run方法,但并不是同一个对象的。如果这个run方法内使用this标记对象,无法产生同步。
  而使用run方法类实现与Runnable则不同。只须要产生一个run方法所在类的对象,然后开启两个Thread线程,并将对象传递进去,所以两个线程使用的run方法是同一个对象的。
  附最简单的死锁程序,说明以上问题:
  1. package com.heima.Demo;

  2. /**
  3. * @author Administrator
  4. *
  5. */
  6. public class Demo{



  7. public static void main(String[] args) {

  8. Deadlock deadlock1 = new Deadlock();
  9. Deadlock deadlock2 = new Deadlock();

  10. deadlock1.deadlock = deadlock2;
  11. deadlock2.deadlock = deadlock1;

  12. deadlock1.start();
  13. deadlock2.start();
  14. }
  15. }

  16. class Deadlock extends Thread {


  17. Deadlock deadlock = null;
  18. @Override
  19. public void run() {

  20. DeadLockMethod();
  21. }

  22. private synchronized void DeadLockMethod() {

  23. System.out.println("锁1:" + Thread.currentThread().getName());

  24. synchronized(deadlock){

  25. System.out.println("锁2" + Thread.currentThread().getName());

  26. }
  27. }
  28. }
复制代码
程序会输出 锁1:Thread-0 锁1:Thread-1 然后死锁。原因,当0线程进去同步函数后,1线程同时进入,因为此时的同步函数DeadLockMethod()使用的是自身this引用,而两个线程,分别使用的是两个Deadlock类不同的对象,所以this也不相同,等无法同步,进而两个线程都进入到同步函数中。分别输出输1和自身线程名,
  而同步代码块中使用的是另一个对象的引用,也就是另一个对象同步函数使用的那个锁,该锁已经被另一个对象持有,结果造成线程死锁.

评分

参与人数 1技术分 +1 收起 理由
Super_Class + 1

查看全部评分

7 个回复

倒序浏览
陈雪琪 来自手机 中级黑马 2013-6-13 23:15:29
沙发
楼主好细心啊~
赞一个!来自: Android客户端
回复 使用道具 举报
建议修改一下细节部分:
public void run() {
synchronized(deadlock){
    DeadLockMethod();
}
}
回复 使用道具 举报
Super_Class 发表于 2013-6-13 23:37
建议修改一下细节部分:
public void run() {
synchronized(deadlock){

  没明白你说的意思,把第二层锁放到函数外?我的是一个同步函数,一个同步代码块,演示的一个死锁问题。如果像你说的把同步代码块放到同步函数外面,那么我还须要在run方法里写打印提示语句,两条输出语句放在两个函数内很不便于程序的修改和美观性
回复 使用道具 举报
slatop@qq.com 发表于 2013-6-14 03:35
没明白你说的意思,把第二层锁放到函数外?我的是一个同步函数,一个同步代码块,演示的一个死锁问题。 ...

建议读一下。你的程序确实实现了死锁,但是是一个线程没有释放锁。死锁并不是一定会发生。死锁是抢占临界资源。如果是多个线程。Thread-0和Thread-1。只用当Thread-0拿到锁1,THread-1拿到锁2,THread--0拿锁2的时候发现Thread-1没有释放锁2,进入阻塞状态(没有释放锁1),同样的Thread-1持有锁2,去拿锁1的时候出现阻塞。
  1.                                         死锁清晰实例
  2.                         ————————————————————————————————————————————————————————————————————                                       
  3.                                                                
  4.                                         class MyLock{
  5.                                                 public static final MyLock locka = new MyLock();
  6.                                                 public static final MyLock lockb = new MyLock();
  7.                                         }

  8.                                         class Demo implements Runnable{
  9.                                                 boolean flag ;
  10.                                                 Demo(boolean flag){
  11.                                                         this.flag = flag;
  12.                                                 }
  13.                                                 public void run(){
  14.                                                         if(flag){
  15.                                                                 synchronized(MyLock.locka){
  16.                                                                         System.out.println(Thread.currentThread().getName()+"..if ..locka..");
  17.                                                                         synchronized(MyLock.lockb){
  18.                                                                                 System.out.println(Thread.currentThread().getName()+"..if ..lockb..");
  19.                                                                                 // try{Thread.sleep(10);}catch(InterruptedException e){}
  20.                                                                         }
  21.                                                                 }
  22.                                                         }
  23.                                                         else{
  24.                                                                 synchronized(MyLock.lockb){
  25.                                                                         System.out.println(Thread.currentThread().getName()+"..else ..lockb..");
  26.                                                                         synchronized(MyLock.locka){
  27.                                                                                 System.out.println(Thread.currentThread().getName()+"..else ..locka..");
  28.                                                                         }
  29.                                                                 }
  30.                                                         }
  31.                                                 }
  32.                                         }

  33.                                         public class ThreadDemo{
  34.                                                 public static void main(String []args){
  35.                                                         Demo d1 = new Demo(true);
  36.                                                         Demo d2 = new Demo(false);
  37.                                                         new Thread(d1).start();
  38.                                                         new Thread(d2).start();
  39.                                                
  40.                                                 }
  41.                                         }                       
复制代码
回复 使用道具 举报
本帖最后由 slatop@qq.com 于 2013-6-14 09:30 编辑
Super_Class 发表于 2013-6-14 06:52
建议读一下。你的程序确实实现了死锁,但是是一个线程没有释放锁。死锁并不是一定会发生。死锁是抢占临 ...


我的就是非常典型的死锁程序,只不过在逻辑处理上使用的是两个this锁,而这也正是很容易忽略的问题,我的死锁和你的死锁,完全是一马事,并不是你所说的,一个线程没有释放。你仔细看就明白了。

同步函数DeadLockMethod()使用的是this锁,而两个线程的this锁并不是同一个对象的(一个线程的同步函数用的是deadlock1标记对象锁,另一个是deadlock2标记对象锁),在线程开启前又分别将deadlock1的引用存入deadlock2的deadlock成员上,而deadlock2的成员deadlock上记录deadlock1的引用,在DeadLockMethod()函数体内使用当前对象的deadlock做为标记设定了一个同步代码块。

也就是说这里的同步代码块上的标记对象锁其实就是另一个线程对象,而同步函数使用的标记对象就是自身的线程对象。

用流程来说,线程1进入同步函数,拿到锁1,线程2进步同步函数拿 到的是锁2,虽然同步函数的标记对象都是this但这个this并不相同,接着线程1去拿同步区的锁,也就是锁2,而锁2已经被线程2拿了。锁2去拿同步区的锁1,结果锁1已经被线程1拿了。

能看明白了吗?我的这种死锁原理和大家平时见的死锁原理一样,只是非常非常非常容易被忽略掉。所以一经发现立即共享给大家了
回复 使用道具 举报
slatop@qq.com 发表于 2013-6-14 09:29
我的就是非常典型的死锁程序,只不过在逻辑处理上使用的是两个this锁,而这也正是很容易忽略的问题,我的 ...

是个建议。学习
回复 使用道具 举报
哥们,厉害
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马