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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 黑马张涛 中级黑马   /  2012-7-16 14:01  /  1727 人查看  /  5 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 zhangtao2005 于 2012-7-16 14:40 编辑

class Single implements Runnable
{
        private static Single s=null;
        Single()
        {
                System.out.println("new Single()");
        }
        public void run()
        {
                        if(s==null)
                        {
                                synchronized(Single.class)
                                {
                                        //if(s==null)
                                        s=new Single();
                                }
                        }
        }        
}
public class LazyMode {
        public static void main(String[] args) {
                Single t=new Single();
                Thread t1=new Thread(t);
                Thread t2=new Thread(t);
                t1.start();
                t2.start();
        }
}
如上代码,在synchronized代码块内将if(s==null)这句注释掉之后,结果有时打印两次new Single(),有时打印三次new Single(),请问这是为什么?

评分

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

查看全部评分

5 个回复

倒序浏览
本帖最后由 刘馨琪 于 2012-7-16 14:21 编辑

多线程问题。
没加那句话时,一个线程可能只new了对象而没有执行完后面代码就挂在那里等待,然后另一个线程进来了,又new了一个对象。一会儿之前的线程醒了,继续执行程序。就会多出一个。
加了那句话后,前一个线程new新对象后,后一个线程一检测发现又对象,就进不去了,就不会多出对象了。
回复 使用道具 举报
首先,你这个代码不是懒汉式
下面就说说打印的情况
public void run()
        {
                        if(s==null)
                        {//t1    t2----->如果两个线程都同时执行到这里的时候失去了执行权,等再次拿到执行权的没有回去判断S==null的条件,执行完同步代码块的代码就会再建两个对象,也就是打印三次"new Single()"  情况2:如果挂在这的线程只是t1或者t2中的其中一个 等拿回执行权的时候就会建立一个S对象 然后另外一个线程再一判断条件s!=null也就不会进这代码里面 这时候就是打印两次
                                synchronized(Single.class)
                                {//当然对应打印三次的情况一个挂在上面的位置其中一个线程挂在这里也是可能的 这没什么太大的区别
                                        //if(s==null)
                                        s=new Single();
                                }
                        }
        }      
public class LazyMode {
        public static void main(String[] args) {
                Single t=new Single();//这是一定会打印的第一次"new Single()"
                Thread t1=new Thread(t);
                Thread t2=new Thread(t);
                t1.start();
                t2.start();
        }
}
回复 使用道具 举报
首先,你这个代码不是懒汉式
下面就说说打印的情况
public void run()
        {
                        if(s==null)
                        {//t1    t2----->如果两个线程都同时执行到这里的时候失去了执行权,等再次拿到执行权的没有回去判断S==null的条件,执行完同步代码块的代码就会再建两个对象,也就是打印三次"new Single()"  情况2:如果挂在这的线程只是t1或者t2中的其中一个 等拿回执行权的时候就会建立一个S对象 然后另外一个线程再一判断条件s!=null也就不会进这代码里面 这时候就是打印两次
                                synchronized(Single.class)
                                {//当然对应打印三次的情况一个挂在上面的位置其中一个线程挂在这里也是可能的 这没什么太大的区别
                                        //if(s==null)
                                        s=new Single();
                                }
                        }
        }      
public class LazyMode {
        public static void main(String[] args) {
                Single t=new Single();//这是一定会打印的第一次"new Single()"
                Thread t1=new Thread(t);
                Thread t2=new Thread(t);
                t1.start();
                t2.start();
        }
}

点评

懂了,谢谢!  发表于 2012-7-16 14:38

评分

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

查看全部评分

回复 使用道具 举报
class Single implements Runnable
{
        private static Single s=null;
        Single()
        {
                System.out.println("new Single()");
        }
        public void run()
        {
                        if(s==null)   
                        {
                                synchronized(Single.class)
                                {
                                        //if(s==null)
                                        s=new Single();
                                }
                        }
        }        
}
public class LazyMode {
        public static void main(String[] args) {
                Single t=new Single();  //  此时会首先执行一次Single类的构造函数,打印一个"new Single()";
                Thread t1=new Thread(t);
                Thread t2=new Thread(t);
                t1.start();此时你开启了了一个线程,加main就是2个线程,线程一开起;main就和t1这个线程抢资源,当t1抢到后就到run方法中,否则向下执行
                t2.start();如果上面是main先抢到资源,运行玩这句话后便是3个线程,于是抢夺cpu的线程就变成了3个
               
               
                当线程进到第一个if的时候如果另一个线程抢到了cpu,那么就停在了这里,下面synchronized(Single.class)就没有执行到,下一个线程进来到run方法中时,第一个线程呛到了cpu资源,此时就有一个new Single,执行完后,第二个线程如果进入到了if语句中,那么就有2哥new Single;如果没有尽到if中,那么此时就只有一个new Single
               
                说了这么多,不知道你看懂没,用语言不好形容  
                总之有几个new  Single就看你的cpu了
        }
}
回复 使用道具 举报
本帖最后由 黑马刘涛 于 2012-7-16 16:00 编辑
  1. class Single implements Runnable
  2. {
  3.         private static Single s = null;
  4.         Single()
  5.         {
  6.                 System.out.println("new Single()");
  7.         }
  8.         public void run()
  9.         {
  10.                         if(s==null)
  11.                         {
  12.                                 synchronized(Single.class)
  13.                                 {
  14.                                      //if(s==null)
  15.          //{
  16.           System.out.println(s == null);
  17.                                         s=new Single();
  18.           System.out.println(Thread.currentThread().getName()+"..."+"running!");
  19.          //}
  20.                                 }
  21.                         }
  22.         }        
  23. }
  24. public class LazyMode {
  25.         public static void main(String[] args) {
  26.                 Single t=new Single(); //主线程创建一个single对象打印一次new single
  27.     System.out.println(Thread.currentThread().getName()+"..."+"running!");
  28.                 Thread t1=new Thread(t);
  29.                 Thread t2=new Thread(t);
  30.                 t1.start();
  31.                 t2.start();
  32.         }
  33. }
复制代码
我添加了一些代码方便观察线程运行情况。先说一句,private static Single s = null;为静态变量,类加载到虚拟机就会初始化为null.
Single t=new Single();// 创建一个Single对象,对象中s == null,后面一句打印输出表示现在主线程正在运行。主线程接着启动两个线程Thread_0和Thread_1
Thread_0运行,进入同步代码块上锁。System.out.println(s == null); //判断为真true
                                        s=new Single(); // 这是s != null
此时输出语句显示Thread_0运行。为什么线程1也运行了,这就涉及到问题的核心即为什么注释掉同步代码块中的if(s==null)有3个线程会运行,而
没注释的话只有两个线程会运行。这是因为当Thread_0运行到同步代码块(上锁)里的时候,线程1也刚好运行到了同步代码块外面但是也经过了第一次判断s==null
而当线程0退出,线程1进入到同步代码块中时运行System.out.println(s == null);会打印false.这时线程1也创建了一个single对象。最后答应了3次new single.

2.如果保留同步代码块中的if(s==null)时,就算线程1运行到同步代码块外面,再次经过一次判断if(s==null)肯定不会执行里面的语句。因此就只有两个线程创建对象.至于运行过程不再赘述,你可以拿我上面的代码试验验证。

捕获.PNG (6.43 KB, 下载次数: 16)

捕获.PNG

评分

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

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马