黑马程序员技术交流社区

标题: 多线程中的同步问题 [打印本页]

作者: forTomorrow    时间: 2015-6-4 19:32
标题: 多线程中的同步问题
大家看看,为什么我加了同步,有时候程序运行也会出现不同步的情况呢
class RunImp implements Runnable {
    public static int count = 0;

    public  void run() {
        while (count < 5) {
            synchronized (this) {
               
                count++;
                System.out.println(Thread.currentThread().getName() + "--------"
                        + count);
            }
        }
    }
}

public class ThreadTest2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Thread(new RunImp()).start();
        new Thread(new RunImp()).start();
    }

}
尝试很多次,有一次运行结果是这样的:
Thread-0--------2
Thread-0--------3
Thread-0--------4
Thread-0--------5
Thread-1--------2
大多情况都同步的


作者: 想要那片海    时间: 2015-6-4 21:06
  1. class RunImp implements Runnable {
  2.     public static int count = 0;

  3.     public  void run() {
  4.         while (count < 5)
  5.         {
  6.         /*1、假设 Thread-0 和Thread-1 在此时同时进入循环体内
  7.          * 2、这时Thread-0可能先获得锁,执行同步代码块中的买票过程。
  8.          * 当Thread-0 运行结束,而Thread-1 已经在循环体内了,没有在回去判断count的取值状态,
  9.          * 而是直接持有锁,执行买票过程,这就有可能造成线程不同步(对共享数据的操作出现偏差)
  10.          * 3、解决:在进入同步代码块内的时候在进行一次count 取值的判断,才能保证共享数据的唯一性
  11.          *           
  12.          * */
  13.             synchronized (this)
  14.             {  
  15.                     while(count<5)//进入同步代码块内在进行一次判断
  16.                     {
  17.                     count++;
  18.                 System.out.println(Thread.currentThread().getName() + "----"+ count);
  19.                     }
  20.             }
  21.         }
  22.     }
  23. }

  24. public class ThreadTest2 {

  25.     public static void main(String[] args) {
  26.         // TODO Auto-generated method stub
  27.         new Thread(new RunImp()).start();
  28.         new Thread(new RunImp()).start();
  29.     }

  30. }
复制代码

作者: 杜黎明    时间: 2015-6-4 21:44
。。路过  抢个沙发
作者: forTomorrow    时间: 2015-6-4 21:51
想要那片海 发表于 2015-6-4 21:06

刚才洗澡时候想了下,你这方法的确解决了同步,避免了打印错误的结果6,但我的那个错误运行结果是怎么来的啊,静态变量啊,怎么没打印出1,反倒是2打印了两次
作者: 程明佳    时间: 2015-6-4 21:55
视频刚看到这,晕呀。。
作者: 谢永烽    时间: 2015-6-4 22:13
好像是 线程都在抢先运行吧,不能按顺序是 狠正常的
作者: 探索者    时间: 2015-6-4 22:24
楼主对count用静态进行共享这能理解,但是楼主在建立多线程的时候,建立了两个对象,那么你所使用的锁的意义是什么,锁有起到作用?两个线程的运行的对象不是同一个
作者: 半月    时间: 2015-6-4 22:35
探索者 发表于 2015-6-4 22:24
楼主对count用静态进行共享这能理解,但是楼主在建立多线程的时候,建立了两个对象,那么你所使用的锁的意 ...

对象不是同一个,但是count是静态的,只有一个啊.
他那个只要把synchronized拿到while外面就可以了
但是我运行是还是会出现2次2,而没有1的情况
刚不清楚是什么原因.百度中:dizzy:
作者: 探索者    时间: 2015-6-4 22:48
半月 发表于 2015-6-4 22:35
对象不是同一个,但是count是静态的,只有一个啊.
他那个只要把synchronized拿到while外面就可以了
但是我 ...

静态是不能解决同步问题的
作者: 探索者    时间: 2015-6-4 22:51
半月 发表于 2015-6-4 22:35
对象不是同一个,但是count是静态的,只有一个啊.
他那个只要把synchronized拿到while外面就可以了
但是我 ...

静态不能解决同步问题的
作者: 探索者    时间: 2015-6-4 22:56
半月 发表于 2015-6-4 22:35
对象不是同一个,但是count是静态的,只有一个啊.
他那个只要把synchronized拿到while外面就可以了
但是我 ...

这是我刚才按楼上提出的解决方案运行的结果:



作者: 半月    时间: 2015-6-4 23:15
探索者 发表于 2015-6-4 22:56
这是我刚才按楼上提出的解决方案运行的结果:

找到问题了。new的是两个对象所以this是不一样的。锁不一样同步也就没有用了。把this改成class就可以了
作者: 武汉小菜鸟    时间: 2015-6-4 23:21
想要那片海 发表于 2015-6-4 21:06

你好,你这样也不对!
他这里是this的问题,加同步锁是为了同一时刻只有一个对象在执行代码块,这里传this代表调用者,谁调用谁执行,所以不能保证同一时刻只有一个对象在执行代码块

解决办法:只需要改变锁对象即可(如:RunImp.class)
作者: 武汉小菜鸟    时间: 2015-6-4 23:22
探索者 发表于 2015-6-4 22:24
楼主对count用静态进行共享这能理解,但是楼主在建立多线程的时候,建立了两个对象,那么你所使用的锁的意 ...

恩,这个才是正解!:):)厉害
作者: 武汉小菜鸟    时间: 2015-6-4 23:25
半月 发表于 2015-6-4 22:35
对象不是同一个,但是count是静态的,只有一个啊.
他那个只要把synchronized拿到while外面就可以了
但是我 ...

这哥们是正解。你传的this,那个线程调用代表那个对吧,那就代表不同的对象。所以你这个不能保证同步。只需改变同步对象。
作者: forTomorrow    时间: 2015-6-5 08:48
武汉小菜鸟 发表于 2015-6-4 23:21
你好,你这样也不对!
他这里是this的问题,加同步锁是为了同一时刻只有一个对象在执行代码块,这里传thi ...

我比较赞同你的观点,我感觉也是this表示的是不同对象,所以导致不是同一把锁,改为类的字节码应该就可以,还没测试
作者: forTomorrow    时间: 2015-6-5 08:57
修改后的代码:

package com.itheima;

class RunImp implements Runnable {
        public static int count = 0;

        public  void run() {
                while (count < 5) {
                        synchronized (RunImp.class) {       
                                if(count>=5)
                                        break;
                                count++;
                                System.out.println(Thread.currentThread().getName() + "--------"
                                                + count);
                        }
                }
        }
}

public class ThreadTest2 {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                new Thread(new RunImp()).start();
                new Thread(new RunImp()).start();
        }

}

作者: forTomorrow    时间: 2015-6-5 09:17
实现方式二:

package com.itheima;

class RunImp implements Runnable {
        private int count = 0;
        public  void run() {
                while (count < 5) {
                        synchronized (RunImp.class) {       
                                if(count>=5)
                                        break;
                                count++;
                                System.out.println(Thread.currentThread().getName() + "--------"
                                                + count);
                        }
                }
        }
}

public class ThreadTest2 {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                RunImp runImp = new RunImp();
                new Thread(runImp).start();
                new Thread(runImp).start();
        }

}

作者: 探索者    时间: 2015-6-5 10:36
forTomorrow 发表于 2015-6-5 08:57
修改后的代码:

package com.itheima;

这个方式是不行的,建立的还是两个对象,虽然你用了唯一的字节码文件作为锁,但对于两个对象,就相对于有两把锁,每一个对象的锁都有唯一的钥匙,但由于不是在同一个对象运行,即使使用的锁为字节码文件,也起不到同步的作用。
作者: 探索者    时间: 2015-6-5 10:42
武汉小菜鸟 发表于 2015-6-4 23:21
你好,你这样也不对!
他这里是this的问题,加同步锁是为了同一时刻只有一个对象在执行代码块,这里传thi ...

如果建立的是两个对象,那么即使改变锁对象为唯一的字节码文件,也是起不到同步的作用
作者: 武汉小菜鸟    时间: 2015-6-5 10:47
探索者 发表于 2015-6-5 10:42
如果建立的是两个对象,那么即使改变锁对象为唯一的字节码文件,也是起不到同步的作用 ...

如果是唯一的字节码,那么同一时刻只能有一个对象访问同步代码块,就能保证安全问题。你试试
作者: 武汉小菜鸟    时间: 2015-6-5 10:51
forTomorrow 发表于 2015-6-5 08:48
我比较赞同你的观点,我感觉也是this表示的是不同对象,所以导致不是同一把锁,改为类的字节码应该就可以 ...

恩,this代表当前对象,谁调用代表谁,所以不能保证同步的安全问题。可以改为字节码试试
作者: 探索者    时间: 2015-6-5 10:56
武汉小菜鸟 发表于 2015-6-5 10:47
如果是唯一的字节码,那么同一时刻只能有一个对象访问同步代码块,就能保证安全问题。你试试 ...

唯一的字节码只能保证在同一个对象运行成立,但对于多个对象,即使都使用的是一样的字节码文件,但也同步不了。不相信可以自己试试,或者在仔细看一下视频
作者: 武汉小菜鸟    时间: 2015-6-5 11:08
探索者 发表于 2015-6-5 10:56
唯一的字节码只能保证在同一个对象运行成立,但对于多个对象,即使都使用的是一样的字节码文件,但也同步 ...

什么叫同步,简单的说,就是某个对象,同一时刻,只能被一个线程访问,那就叫同步。(我在网上查的)。“唯一字节码保证一个对象访问”,那就能保证同步了。
可能是你我对于同步的理解有点问题。
作者: 探索者    时间: 2015-6-5 11:28
武汉小菜鸟 发表于 2015-6-5 11:08
什么叫同步,简单的说,就是某个对象,同一时刻,只能被一个线程访问,那就叫同步。(我在网上查的)。“ ...

刚才想了一下,你的理解是对的,只是之前在程序了试了唯一字节码对多个对象的情况,发现也有不同步,所以就理解错了,当你提出你的理解,其实自己也不能解释字节码在内存中是唯一的,为什么不能保证多个对象同步?后来有看了一下程序,发现是程序的有点问题,所以是我的理解错了
作者: Always,    时间: 2015-6-5 11:30
                 学习中
作者: 武汉小菜鸟    时间: 2015-6-5 11:55
探索者 发表于 2015-6-5 11:28
刚才想了一下,你的理解是对的,只是之前在程序了试了唯一字节码对多个对象的情况,发现也有不同步,所以 ...

恩恩,多多探讨,大家才能共同进步,加油
作者: pp7803515    时间: 2015-6-5 13:58
同步的意思不是说。例如我多个窗口在卖火车票(也就是多线程的时候,调用同一个对象),我A窗口和B窗口出火车票的时候,不能出现同一个座位的火车票吗?
作者: pp7803515    时间: 2015-6-5 14:00
new Thread(new RunImp()).start();
        new Thread(new RunImp()).start();
你这里写的是2次,如果我改为
   RunImp ri= new RunImp();
                new Thread(ri).start();
                new Thread(ri).start();
这样的话调用的应该就是同一个对象了,而不是不同对象了吧。不知道我的理解有没有错
作者: hellotaomi    时间: 2015-6-5 14:01
new了两个线程,两个线程对应的对象就不一样吧,那怎么还能用this作为说同步锁的对象呢
作者: forTomorrow    时间: 2015-6-5 14:25
pp7803515 发表于 2015-6-5 14:00
new Thread(new RunImp()).start();
        new Thread(new RunImp()).start();
你这里写的是2次,如果我 ...

理解的很正确
作者: forTomorrow    时间: 2015-6-5 14:27
hellotaomi 发表于 2015-6-5 14:01
new了两个线程,两个线程对应的对象就不一样吧,那怎么还能用this作为说同步锁的对象呢 ...

嗯 说的对 是两个对象 所以应该是用类名.class作为锁旗标,this表示对象锁
作者: yang2015    时间: 2015-6-5 22:58
还没学到,先学习学习。。。。




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