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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 杨远 中级黑马   /  2013-1-5 15:38  /  2586 人查看  /  5 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 杨远 于 2013-1-8 00:09 编辑

在学习初期遇到的问题,因为家里没网,就先记下了。现在发出来,求解惑。。。

问题描述:这是我在写死锁代码时遇到的问题,但是问题不是关于死锁的,而是对象建立和new关键字的问题。
代码如下:
  1. class Test
  2. {
  3.         public static void main(String[] args)
  4.         {
  5.                 new Thread(new DeadLock(true)).start();
  6.                 new Thread(new DeadLock(false)).start();
  7.         }
  8. }
  9. class DeadLock implements Runnable
  10. {
  11.         Object obj1 = new Object();
  12.         Object obj2 = new Object();
  13. //        private Object obj1;
  14. //        private Object obj2;
  15.         private boolean flag;

  16.         DeadLock(boolean flag)
  17.         {
  18.                 this.flag = flag;
  19. //                obj1 = new Object();
  20. //                obj2 = new Object();
  21.         }

  22.         public void run()
  23.         {

  24. //             测试10。
  25. //                int x = 1;
  26. //                while (x<2)
  27. //                {
  28. //                        System.out.println(obj1.equals(obj2));
  29. //                        System.out.println(obj2.equals(obj1));
  30. //                        x+=1;
  31. //                }

  32.                 if(flag)
  33.                 {
  34.                         while(true)
  35.                         {
  36.                                 synchronized(obj1)
  37.                                 {
  38.                                         System.out.println(Thread.currentThread().getName()+"..if obj1 ");
  39.                                         synchronized(obj2)
  40.                                         {
  41.                                                 System.out.println(Thread.currentThread().getName()+"..if obj2");                                       
  42.                                         }
  43.                                 }
  44.                         }
  45.                 }
  46.                 else
  47.                 {
  48.                         while(true)
  49.                         {
  50.                                 synchronized(obj2)
  51.                                 {
  52.                                         System.out.println(Thread.currentThread().getName()+"......else obj2");
  53.                                         synchronized(obj1)
  54.                                         {
  55.                                                 System.out.println(Thread.currentThread().getName()+"......else obj1");
  56.                                         }
  57.                                 }
  58.                         }
  59.                 }
  60.         }
  61. }
复制代码
/*

测试:
1.        不用静态修饰,synchronized后使用obj1,obj2。
        结果:编译通过,运行无法出现死锁。

2.        将定义的两个对象(锁)obj1,obj2用static修饰,然后用类名调用。(老师示例做法)
        结果:编译通过,可以死锁。

3.        将两个锁改为 DeadLock.class和 Test.class 。
        结果:编译通过,可以死锁。
        把其中一个如 DeadLock .class 换成非静态的 obj1 。
        结果:编译通过,运行无法出现死锁。

4.        synchronized后的括号里为空。
        结果:编译失败。
        Test.java:24: 错误: 非法的表达式开始
                                                                        synchronized()
                                                                                                 ^
        1 个错误

5.        改为 x 。
        结果:编译失败。
        Test.java:32: 错误: 找不到符号
                                                                        synchronized(x)
                                                                                                 ^
          符号:   变量 x
          位置: 类 DeadLock
        1 个错误

6.        定义变量 int m; int n; (boolean,char等相同情况)
        结果:编译失败。
        Test.java:86: 错误: 意外的类型
                                                                        synchronized(m)
                                                                        ^
          需要: 引用
          找到:    int

7.        定义引用数据类型的变量。数组。
        结果:编译通过,运行无法出现死锁。

8.        定义 String m; String n;
        结果:编译通过,提示空指针异常。
        Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NullPointerException

                        at DeadLock .run(Test.java:110)
                        at java.lang.Thread.run(Thread.java:722)
        java.lang.NullPointerException
                        at DeadLock .run(Test.java:96)
                        at java.lang.Thread.run(Thread.java:722)

        赋值后。 String m = "0"; String n = "1";
        结果:编译通过,可以死锁。
        改为 String m = "1"; String n = "1";
        结果:编译通过,运行无法出现死锁。

9.        在DeadLock 类中建立对象:DeadLock  a = new DL();        DeadLock  b = new DL();  synchronized的锁换成a和b。
        结果:编译通过,运行出现Error。
        Exception in thread "main" java.lang.StackOverflowError
                        at DL.<init>(Test.java:48)
                        at DL.<init>(Test.java:48)
                        at DL.<init>(Test.java:48)
        在以前的视频里好像见过这个Error,但是忘记了这是什么!

        12.13日看到第20天07的视频中,也出现了这样的异常。知道了,StackOverflowError是内存溢出。

10.        在run方法内同步代码块前面加上这样一段代码
                int x = 1;
                while (x<2)
                {
                        System.out.println(obj1.equals(obj2));
                        System.out.println(obj2.equals(obj1));
                        x+=1;
                }
        这样,要调用方法,对象肯定会创建的吧?
        但是运行后,这两条比较的输出语句有输出,但是仍然没有出现死锁。


主问题:
问题与分析:
1是我最开始的写法,对比老师的示例后发现,差别在于我是定义类然后直接调用,老师的是静态后类名调用。
        对于一直运行无法出现死锁的情况。那么应该是两种情况:没有锁,或者锁是同一个。
        对于是否是锁相同,由3可知,问题在于obj1 没有锁的功能。
        回头想,这可能是我的Java基础哪里出现了问题。
        从上面推出应该是这句“Object obj1 = new Object();”不在主线程中所有其实并没有建立对象。
        但是通过不同情况的试验就发现以下几点不明白:
                通过4和5可以看出,Object obj1 这个定义变量是实现了的。
                有new关键字。为什么没有建立对象呢?
                如果建立了,那么不同类中可以static修饰后用类名调用对象,同类中为什么不可以直接调用呢?

总结与思考:
要关注的:
试验2和试验8的道理。
解释:
        2:        类都有自己对应的字节码文件对象。
        8:        对象作为锁的功能时需要被判定是否相同
                        <--对象是否相同,即是否在同一个堆内存中。
                                        <--判定对象所在堆内存的地址,即比较数值。
                String判定的也是数值是否相同。所有String可以用作同步的锁。
                或者说这个也是对象,String m = new String("0");
                //第二天正好看到API这里。所有的双引号括起 "x" 都是对象,就直接用 "0" 和 "1" 作为锁,没有问题。

要注意的:
试验9的Error。所反应出的问题。
思考:哪些情况会导致内存溢出(StackOverflowError)?

另外一个问题:
        经过测试
        new Thread(new DL(true)).start();
        new Thread(new DL(true)){}.start();
        都可以正确运行。
那么这两个的区别是什么?

*/








点评

赞一个  发表于 2013-1-5 15:57

评分

参与人数 2技术分 +1 黑马币 +30 收起 理由
Rancho_Gump + 30 赞一个!
郑传庆 + 1 神马都是浮云

查看全部评分

5 个回复

倒序浏览
求深刻指点,求彻悟。。
回复 使用道具 举报
实现同步有3种方式:1:同步块;2:同步方法;3静态同步方法。
而你这里用的就是同步块,也就是两个对象,死锁也就是同步嵌套调用产生的(这3种也都可以产生死锁),拥有A对象的需要B对象,拥有B对象的需要A对象,这样就产生死锁了。至于你说的静态的两个对象,确实可以解决问题,但一般不会这么做,你可以将这两个对象当成一个属性,不赋值,到后面在方法里面才赋值,也是可以的,静态不静态一般都是能锁上的,只是你想要一次性就锁好,最好在调用一个对象后加上一些休眠语句,也就是sleep(),这就能保证一次性锁好,否则你的程序太小了,CPU弄完根本不需要什么时间,虽然理论上可以,但你锁N次都不一定能成功。

多线程也是两种方式,一个是写一个Thread类的子类,里面重写run()方法,然后开启这个线程;还一个就是写一个任务类实现Runna接口,重写里面的run()方法,然后创建一个线程,把任务对象放进去,再开启线程。一般用一种就可以了。
所以呢,你的2种方式,其实都是是实现Runnable接口的方式,都创建Thread类的对象,只是第2个画蛇添足了,你用了内部类的方式去继承Thread类,但是你有啥都没做,然后还是去实现Runnable接口,也就是你第2个创建的线程对象是Thread类的子类对象。

评分

参与人数 1技术分 +1 收起 理由
Rancho_Gump + 1 赞一个!

查看全部评分

回复 使用道具 举报
肖志锋 发表于 2013-1-5 19:17
实现同步有3种方式:1:同步块;2:同步方法;3静态同步方法。
而你这里用的就是同步块,也就是两个对象, ...

谢谢,很详实,受用了。
也就是说我贴的代码是可以死锁的,只是几率低的问题。
可以讲讲new这个关键字吗:D
回复 使用道具 举报
本帖最后由 肖志锋 于 2013-1-7 21:36 编辑

new基本都是创建对象来用的,后面一般都是跟着构造函数,也可以是数组的,这两个是一起,不分开的。比如说,new String(),new ArrayList(),。new int[]。。什么之类的,当然也可以是自定义的,包括引用类型的数组和类。

评分

参与人数 1黑马币 +8 收起 理由
杨远 + 8 ok,thx

查看全部评分

回复 使用道具 举报
在Java中,形如这样的代码:
String str;
定义一个String类型的引用,但它只是一个引用,并没有实际在堆中分配空间。
当使用这样的代码:
str = new String("Hello!");
就在堆中分配了空间。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马