public class ViolentAccessDemo
{
public static void main(String args[])
{
ViolentObject violentObject = new ViolentObject();
Thread thread1 = new Thread(new ThreadAccess(violentObject));
Thread thread2 = new Thread(new ThreadAccess(violentObject));
thread1.start();
thread2.start();
for(int i=0;i<10;i++)
{
violentObject.print();
try{
Thread.sleep(500);
}catch(InterruptedException e)
{

}
}

}
}
class ViolentObject
{
private int x;
private int y;
public void increase()
{
x++;       //我的理解是thread1执行到x++;没有执行y++退出。
y++;        //thread2接着还是执行x++,这样的话,输出x的值肯定是x大才对,可是输出结果:
}
public void print()
{
System.out.println("x="+x+","+"y="+y);
}
}
class ThreadAccess implements Runnable
{
private ViolentObject violentObject;
public ThreadAccess(ViolentObject vob)
{
System.out.println("this is constructor of ThreadAccess");
this.violentObject = vob;
}
public void run()
{
while(true) violentObject.increase();
}
}
输出结果:
this is constructor of ThreadAccess
this is constructor of ThreadAccess
x=0,y=0
x=84050630,y=89506803  //y居然比x大,这不违反规律了吗?
x=165934856,y=166907497
x=250595453,y=257923836
x=336504131,y=328009964
x=432364945,y=416151003

解决方案 »

  1.   

    因为你的使用的是同一个violentObject 对象,两个线程同时改变这个y值,
      

  2.   

    x++ => tmp = x + 1;   //A
           x = tmp;       //B当Thread1执行完A后,Thread2开始执行,假设此时x=30。
    Thread2执行一段时间后,x=100, y=100,此时Thread1继续,执行B,此时tmp=31。于是出现了y > x。
      

  3.   

    ls的...两个线程他也是先到x在到y啊.
      

  4.   

    刚才说的lss,ls的我看了.似乎想起来看corejava的时候说过java很多操作不比如i++不是原子操作.是分两步的..所以多线程的时候可能会有不可预期的结果.
      

  5.   

    加锁并且同步,问题还的存在,只是数据的误差没有你的这个大,只是到了一定的时候才会发现数据存在误差,但是之后又回复一致,也就是x和y的值相等,我想,这个是虚拟机调度线程是不确定的,因为你的把一个线程休眠了500毫秒,再让其复活,到底是谁进入runnable状态,还得等虚拟机来分配,这个分配是根据级别和当前的等待状态来决定,及时你的线程进入了runnable状态,也不一定得到run,这就是虚拟机调度线程的时候存在的不确定性.
      

  6.   

    这是由于,你的ViolentObject类所对应的对象,不是线程安全的,而造成的。如果将ViolentObject的两个方法都添加Synchronized关键字,就可以x与y的值相等。出现这种状况(x>y),具有随机性。我们知道,对一个变量做++运算的时候,这个变量会先从内存中读取到CPU的寄存器当中,++运算后,再将寄存器的值,回写到内存当中。那么,在多线程对同一变量做++运算的时候,会出现数据的不同步现象。我们知道,线程在操作系统中,是按照时间片来运行的。如果线程1,刚把x变量,从内存中读到寄存器中,这时,时间片用完了,之后,线程2运行,把x从内存中读到寄存器,完成++运算,又回写到内存,
    这样,当线程1重新获得时间片,继续刚才的++操作,然后再回写到内存,
    此时,2个线程都对x做了++运算,
    但是,由于两次从内存取值都是相同的所以,结果,相当于只完成了一次运算。由于上述现象的产生具有不确定性,所以,两个变量x、y,它们的大小,也具有不确定性。使用synchronized关键字,就会将相对应的对象上锁,当操作线程没有是放对应的锁时,其他线程是不可能访问的。楼主还可以试一试,将x、y两个变量,在声明的时候,都添加volatile关键字,效果,可能要不什么都不加要好。
    但是,添加volatile关键字的方法,无法保证在输出结果是x与y相同的值,
    因为每次对两个变量都是++操作,那么,输出x与y的差值应该不超过4.
    当然,不超过4只是我的理论推测,楼主有兴趣,可以试一下。哈哈。
      

  7.   

    看来关注这个问题的人比较多
    不过,我研究了下,还是觉得JAVA是按行解释执行的。
    不可能y会执行得比x多
      

  8.   

    总共是3个线程在运行.
    因为存取的是同一个对象,
    System.out.println("x="+x+","+"y="+y)这条语句
    main线程输出x和y的值之间有个时间差,在此期间另外两个线程仍然在进行++操作.
    所以当打印y的值时就比x大了.
      

  9.   

    x++;
    y++; 
    这里的x和y值的更新次序是存在颠倒的可能的。加上volatile关键字就可以保证j的值永远不会大于i的值。     
      

  10.   

    是存在这个问题,不过我在程序中运行了,只要print方法是synchronized的就能保证x>=y的结果了所以应该是读的时候存在y仍在++的问题
      

  11.   

    执行 System.out.println....语句时,首先取X的值,再取Y的值,再输出。如果计算机进行取值这个指令需要单位时间为10,假定计算机从0这个时间点取X的值,10个单位的时间过去后X的值被取出暂存,在这个过程中两个线程并没有停,而假定他们的运行时间为单位时间1,那么在你取到X的值后,两个线程又执行了N次,当你再取到Y值时,他的值自然比X要大了许多!
      

  12.   

    可以参考一下!值得借鉴!
    不错 谢谢
    顶一下.谢谢.
    回帖是一种美德!
    UP 
    UP
    努力的学习学习!!!期待该问题的发展。
    好东西啊,我顶
    顶顶
    好用么?先下下来看看。这种精神值得学习。
    多谢了,学下先。
      

  13.   

    只要修改一下ThreadAccess类中的run方法就可以了。public void run() {
      while (true){
        synchronized(violentObject){
    violentObject.increase();
        }
      }
    }
      

  14.   

    把你的INC方法分成俩,INCX,INCY。
    然后按你的测试。发现1:x 2
    2:x 2
    1:y 1
    2:y 2
    1:x 3
    1:y 3
    2:x 4
    2:y 4
    1:x 5
    2:x 5
    2:y 6
    1:y 5
    1:x 6
    2:x 7
    2:y 7
    1:y 8
    2:x 8
    2:y 9
    1:x 8
    1:y 10
    2:x 10
    2:y 11
    1:x 10
    1:y 12
    2:x 11
    2:y 13
    1:x 12
    1:y 14
    1:x 13
    1:y 15
    main over.
    2:x 14
    2:y 16很显然。进行到4~5秒的时候。俩线程开始混运行了。
    引用中说的安全性很重要。如果不能确保对象的同步性,就尽量不要用多线程。
      

  15.   

    #36好像对了Thread thread1 = new Thread(new ThreadAccess(violentObject));
            Thread thread2 = new Thread(new ThreadAccess(violentObject));
            thread1.start();
            thread2.start();
    启动了线程, 以后就由操作系统按照自己的理解运行它了。
    你不能因为是
    thread1.start();
    thread2.start(); 就要求OS先运行thread1再运行thread2。
    thread1.start();
    thread2.start();

    thread2.start();
    thread1.start();
    都是一样的结果(都混乱)。java里用 Synchronized关键字来完成线程的同步。
    VC里面常用 EVENT配合WaitForSingleObject()函数来达到同步效果。
      

  16.   

    刚接触JAVA ·· 学习中   顶下了
      

  17.   

    总共是3个线程在运行. 
    因为存取的是同一个对象, 
    System.out.println("x="+x+","+"y="+y)这条语句 
    main线程输出x和y的值之间有个时间差,在此期间另外两个线程仍然在进行++操作. 
    所以当打印y的值时就比x大了.