黑马程序员技术交流社区

标题: 多线程的安全问题,加了同步代码块还是木有解决 [打印本页]

作者: 宋美成    时间: 2014-4-29 00:23
标题: 多线程的安全问题,加了同步代码块还是木有解决
本帖最后由 宋美成 于 2014-4-29 12:00 编辑

复制代码
就是毕老师的多个线程卖票的问题,我加了同步代码块,但是在运行时还是出现了一张票卖两次,还有-1号与0号票的问题,求指导,求拯救
  1. class Ticket extends Thread
  2. {
  3.            private static int tick =100;
  4.            public void run()
  5.            {
  6.                while (true)
  7.               {
  8.                       synchronized(new Object())//同步代码块
  9.                       {
  10.                                if (tick>0)
  11.                                {
  12.                                     try{Thread.sleep(10);}
  13.                                     catch(Exception e){}
  14.                                     System.out.println(Thread.currentThread().getName()
  15.                                                                     +"...sale"+tick--);
  16.                                }
  17.                       }
  18.               }
  19.           }
  20. }
  21. public class TicketDemo
  22. {
  23.            public static void main(String[] args)
  24.            {
  25.                   Ticket t1=new Ticket();
  26.                   Ticket t2=new Ticket();
  27.                   Ticket t3=new Ticket();
  28.                     t1.start();
  29.                     t2.start();
  30.                     t3.start();
  31.             }
复制代码
我查过了,代码块加的是对的啊,无语了

作者: Silvester    时间: 2014-4-29 01:15
本帖最后由 Silvester 于 2014-4-29 09:50 编辑

楼主,你的主函数没错,是我大意了,楼主的Ticket是继承Thread类的,而我本能的认为是Ticket实现了Runnable接口了,抱歉哈,看了楼下的回复自己检查了才反应过来,感谢楼下,共勉!

  1. public class TicketDemo {
  2.         public static void main(String[] args) {                           
  3.                
  4.                 Ticket t = new Ticket();
  5.                                 
  6.                 Thread t1 = new Thread(t);//创建线程
  7.                 Thread t2 = new Thread(t);
  8.                 Thread t3 = new Thread(t);
  9.                                 
  10.                 t1.start();
  11.                 t2.start();
  12.                 t3.start();               
  13.         }
  14. }
复制代码


还有同步的问题主要是在同步代码块的地方,synchronized(new Object())这里出问题了,new Object( )不应该放在这个位置,应该在run( )方法之前定义一个Object obj = new Object( );,把obj作为参数传递给synchronized,即synchronized(obj),代码如下,楼主出错的地方主要在我添加注释的地方:

  1. class Ticket implements Runnable {     
  2.         private int tick = 100;
  3.         Object obj = new Object();//注意这里
  4.         public void run() {
  5.                 while(true){
  6.                         synchronized(obj) {//这里obj直接传入
  7.                                 if (tick>0) {
  8.                                         try {
  9.                                                 Thread.sleep(10);
  10.                                         } catch (Exception e) {

  11.                                         }
  12.                                         System.out.println(Thread.currentThread().getName()+"...sale: " + tick--);
  13.                                 }
  14.                         }
  15.                 }
  16.         }
  17. }
复制代码


        至于为什么不能在synchronized( )里面直接new Object( ),是因为obj其实是作为互斥信号量(操作系统的一个名词),传入的,只能简单的说,obj当传入的时候不能被改变,要保证其唯一性,才能有效控制安全,也就是楼下所说的同一个锁的问题。
        而如果你使用的是synchronized(new Object( ) ),即每次线程调用run( )方法的时候,t1.start()的run()就new Object( ),t2.start()的run()又重新new Object( ),t3.start()的run()第三次new Object( ),如果t1,t2,t3同步的部分使用的obj是三个不同的obj的话,互相之间变不会被影响,也就无法相互牵制,所以要提前定义好obj,让t1,t2,t3的同步代码块使用的是同一个obj。
        不知这么解释楼主能否看明白,希望能够对楼主有帮助。
作者: 谢振宣    时间: 2014-4-29 02:47
  1. /*
  2. class Ticket extends Thread
  3. {
  4.         private static int tick =100;
  5.         public void run()
  6.         {
  7.                 while (true)
  8.                 {
  9.                         synchronized(new Object())//同步代码块  //此处的锁不是同一把锁
  10.                         {
  11.                                 if (tick>0)
  12.                                 {
  13.                                         try{Thread.sleep(10);}
  14.                                         catch(Exception e){}
  15.                                         System.out.println(Thread.currentThread().getName()+"...sale"+tick--);
  16.                                 }
  17.                         }
  18.                 }
  19.         }
  20. }
  21. public class TicketDemo
  22. {
  23.         public static void main(String[] args)
  24.         {
  25.                 Ticket t1=new Ticket();
  26.                 Ticket t2=new Ticket();
  27.                 Ticket t3=new Ticket();
  28.                 t1.start();
  29.                 t2.start();
  30.                 t3.start();
  31.         }
  32. }
  33. */

  34. //换一把锁就可以了,可以是Ticket.class,也可以是TicketDemo.class,只要是程序运行时一直存在的对象即可
  35. //以下修改后的程序,执行时是正常的

  36. class Ticket extends Thread
  37. {
  38.         private static int tick =100;
  39.         public void run()
  40.         {
  41.                 while(true)
  42.                 {
  43.                         synchronized(Ticket.class)//此处换了一把统一的锁,相当于用一把锁,锁了三扇门
  44.                         {
  45.                                 if (tick>0)
  46.                                 {
  47.                                         try{Thread.sleep(10);}catch(Exception e){}
  48.                                         System.out.println(Thread.currentThread().getName()+"...sale...."+tick--);
  49.                                 }
  50.                         }
  51.                 }
  52.         }
  53. }
  54. public class TicketDemo
  55. {
  56.         public static void main(String[] args)
  57.         {
  58.                 Ticket t1=new Ticket();
  59.                 Ticket t2=new Ticket();
  60.                 Ticket t3=new Ticket();
  61.                 t1.start();
  62.                 t2.start();
  63.                 t3.start();
  64.         }
  65. }
复制代码

作者: 875588381    时间: 2014-4-29 09:26
主要还是同步锁的问题,你这里使用了new Object(),
自己仔细想想,创建了3个线程,每个线程同步的时候,都会创建一把新的锁,这样怎么能同步呢?
解决方法同楼上所述,换成class字节锁即可。或者先定义obj对象,再锁定也可。

synchronized(new Object())//同步代码块

                      {

                               if (tick>0)

                               {

                                    try{Thread.sleep(10);}

                                    catch(Exception e){}

                                    System.out.println(Thread.currentThread().getName()

                                                                    +"...sale"+tick--);

                               }

                      }


作者: 微笑=.一瞬间    时间: 2014-4-29 10:07
//由于你同步代码快中的锁不是同一个,没个线程执行run方法是就会出现一个锁对象
// 我帮你换了一个锁 你再运行看看
class Ticket extends Thread
{
           private static int tick =100;
           public void run()
           {
               while (true)
              {
                      synchronized(Ticket.class)//同步代码块
                      {
                               if (tick>0)
                               {
                                    try{Thread.sleep(10);}
                                    catch(Exception e){}
                                    System.out.println(Thread.currentThread().getName()
                                                                    +"...sale"+tick--);
                               }
                      }
              }
          }
}
public class TicketDemo
{
           public static void main(String[] args)
           {
                  Ticket t1=new Ticket();
                  Ticket t2=new Ticket();
                  Ticket t3=new Ticket();
                    t1.start();
                    t2.start();
                    t3.start();
            }
}

作者: 宋美成    时间: 2014-4-29 12:00
谢振宣 发表于 2014-4-29 02:47

恩恩,谢谢,明白了
作者: 宋美成    时间: 2014-4-29 12:17
Silvester 发表于 2014-4-29 01:15
楼主,你的主函数没错,是我大意了,楼主的Ticket是继承Thread类的,而我本能的认为是Ticket实现了Runnable ...

你好,我先定义了obj,然后在才传入,结果还是没有用,依然出现0和-1的情况
作者: 宋美成    时间: 2014-4-29 12:19
谢振宣 发表于 2014-4-29 02:47

您好,我的锁用了字节码对象,安全问题是解决了,不过我运行N便,始终只有一个线程在跑,这个情况我觉得不是偶然啊,您那的运行结果正常吗?
作者: Silvester    时间: 2014-4-29 13:10
本帖最后由 Silvester 于 2014-4-29 13:16 编辑
宋美成 发表于 2014-4-29 12:17
你好,我先定义了obj,然后在才传入,结果还是没有用,依然出现0和-1的情况 ...

不是吧,我测了20多遍了,都没有出现问题,是不是哪里的代码出问题了,附上我运行的代码,其实这里传入的obj对象和楼主换成的字节码对象的道理是一样的,只是我的Ticket是实现Runnable接口而不是直接继承Thread类,这样运行时候多个线程的交替也出来了,楼主可以对照一下看看:
  1. class Ticket implements Runnable {
  2.            private static int tick =100;
  3.            Object obj = new Object();
  4.            public void run() {
  5.                while (true) {
  6.                       synchronized(obj) {
  7.                                if (tick>0) {
  8.                                     try {
  9.                                              Thread.sleep(10);
  10.                                      } catch(Exception e) { }
  11.                                System.out.println(Thread.currentThread().getName()+"...sale"+tick--);
  12.                                }
  13.                       }
  14.               }
  15.           }
  16. }

  17. public class TicketDemo {
  18.         public static void main(String[] args) {                           
  19.                
  20.                 Ticket t = new Ticket();
  21.                                 
  22.                 Thread t1 = new Thread(t);
  23.                 Thread t2 = new Thread(t);
  24.                 Thread t3 = new Thread(t);
  25.                                 
  26.                 t1.start();
  27.                 t2.start();
  28.                 t3.start();               
  29.         }
  30. }
复制代码


作者: 宋美成    时间: 2014-4-29 13:21
Silvester 发表于 2014-4-29 13:10
不是吧,我测了20多遍了,都没有出现问题,是不是哪里的代码出问题了,附上我运行的代码,其实这里传入的o ...

亲测,给力,我的eclipse闹情绪,重启了就对了,谢谢了
作者: 谢振宣    时间: 2014-4-29 13:24
宋美成 发表于 2014-4-29 12:19
您好,我的锁用了字节码对象,安全问题是解决了,不过我运行N便,始终只有一个线程在跑,这个情况我觉得 ...

D:\javawork\TicketDemo.jpg
作者: 宋美成    时间: 2014-4-29 13:28
谢振宣 发表于 2014-4-29 13:24

已经搞定,谢谢你了




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2