首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

关于线程同步的代码简单小例子,过了一年,还是有关问题

2011-11-15 
关于线程同步的代码简单小例子,过了一年,还是问题。Java codepublic class ViolentAccessDemo {public stat

关于线程同步的代码简单小例子,过了一年,还是问题。

Java code
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;   private int z;   private int a;   public void increase(){  //这里输出的值是不一样,因为            //x++ 在刚被执行完之后,就退出时间片,而下次进来的线程又重新执行x++。           //那么肯定地说x的值加到最后,必然是最大的。可是事实上,并不是如此。            //加最一个synchronized是不是保证这句代码被执行完解锁,再退出CPU时间片。           //很不解。      x++;      y++;      z++;      a++;   }   public void print(){      System.out.println("x="+x+",y="+y+",z="+z+",a="+a);   }}class ThreadAccess implements Runnable{   private ViolentObject violentObject;   public ThreadAccess(ViolentObject violentObject){      this.violentObject = violentObject;   }   public void run(){      while(true) violentObject.increase();   }}


[解决办法]
//这里输出的值是不一样,因为
//x++ 在刚被执行完之后,就退出时间片,而下次进来的线程又重新执行x++。
//那么肯定地说x的值加到最后,必然是最大的。可是事实上,并不是如此。
//加最一个synchronized是不是保证这句代码被执行完解锁,再退出CPU时间片。
//很不解。

你可以在x++ 后面加个
try{
Thread.sleep(50);
}catch(InterruptedException e){}
这样就可以更好的让一个时间片到期。
看看,这样效果就出来了,显示x的值最大。
[解决办法]
我打出来的结果的确是x最大啊,楼主你的什么结果?
[解决办法]
看不到有什么线程同步和控制的代码呀。

而且
public void increase()
这里没有同步标识,你们的不是一个atomic的调用,当然有问题。完全是听天由命式的多线程并发处理。
[解决办法]
如果楼主只是想要看到x的值最大的话,可以在x++之后睡眠,这样另一个线程就有机会得到时间片执行

对于加上synchronized,只能保证synchronized的方法一次执行完后释放锁,但不能保证别的方法比如说你这里的public void print()不能访问其中的x,y,z和a,所以看不出来x的值最大
[解决办法]
问题可能出在print这里,因为没有同步所以print取得的数据也是在变化的

你拿每一个数字的时候,他们都在增长啊
[解决办法]
探讨
每个线程拿到的时间片均是重新开始执行。
x++ y++ z++ a++
顺序过来,是吗?
还是记着我上次执行了x++,再执行y++???


[解决办法]
如果你想把线程理解透的话最好看看操作系统。
[解决办法]
抢用资源的造成的不同步:

x=0 y=0 z=0 a=0
线程A:拿到x=0
线程A:自增,x=1

线程B:拿到x=0
线程B:自增,x=1

线程A:写回x=1
线程B:写回x=1
[解决办法]
改下,这种可能性比较大……

线程A:拿到x=0

线程B:拿到x=0
线程B:自增,x=1
线程B:写回x=1

线程A:自增,x=1
线程A:写回x=1

[解决办法]
还是没说清楚,假设修改可以立即返回……

线程A:拿到x=0

线程B:拿到x=0
线程B:自增,x=1

线程A:自增,x=1
[解决办法]
x++;
执行过程如下:
Java code
1    ALOAD 02    DUP3    GETFIELD ViolentObject.x : I4    ICONST_15    IADD6    PUTFIELD ViolentObject.x : I
------解决方案--------------------


探讨
如果加了synchronized
A{1
  2
  3  //执行到第三行,时间片用完退出,那怎么办》B线程进来了?还是A通知B不要来了??
  4
  5

[解决办法]
public synchronized void increase()是一个同步方法,锁就是对象本身

线程A执行violentObject.increase,拿到锁 "violentObject"
假如执行到一半时间片用完,线程B也无法调用,因为执行这个方法就要去拿"violentObject"
要等到A执行完互斥段释放锁,B才可以调用
[解决办法]
探讨
打一个非常严酷的比方:
A执行完了,想交出执行权,但恰好时间片没用光,又接着执行到一半退出,那B只能说命不好,永远都执行不了。
不知道谁能反驳我的观点。。。

[解决办法]
探讨
打一个非常严酷的比方:
A执行完了,想交出执行权,但恰好时间片没用光,又接着执行到一半退出,那B只能说命不好,永远都执行不了。
不知道谁能反驳我的观点。。。

[解决办法]
为什么x 的值不是最大原因是这样的。
1:程序运行时有三个线程:thread1 ,thread2,主线程。
thread1 和thread2启动时,死循环执行:
while(true)
{
violentObject.increase();

}
即使主线程停止执行,这两个线程后台继续执行。当主线程执行print()方法时,打印x的值,它必须取出x在内存中的值,当它打印完x时,在这段时间间隔,thread1,thread2可能已经把y的原来的值(即取出x值时对应的y值)给改变掉了(不断增加的)。如果时间间隔小,cpu取出的值可能还是原来的值。所以有可能x值不是最大。如果increase()方法是threadsafe的话应该是<=y,z,a的值。


synchronized,lock,atomic 只是保证寄存器,内存中的值一致性。
[解决办法]
对楼主的问题只是看了看代码,如果楼主要保证同步,除了必要的synchronized外,实例变量还需要volatile 
楼主可以看看java的内存模型
[解决办法]
主要问题出在print方法的效率太低了,因为采用了字符串连接的方式,计算"x=" +x的时候,y已经被其它的线程更改过了,而计算 "x=" + x + ",y=" + y的时候 z又被更改过了,这样看起来就是后面的数字比前面的大。
如果把print函数改成如下:
public void print() {
System.out.println("a=" + a + ",z=" + z + ",y=" + y + ",x=" + x);
}
看到的就是 a<z<y<x
把print函数改成如下:
public void print() {
int a1 = x;
int a2 = y;
int a3=z;
int a4=a;
System.out.println("x=" + a1 + ",y=" + a2 + ",z=" + a3 + ",a=" + a4);
}
看到的就是x=y=z=a
[解决办法]
探讨
x++;
执行过程如下:
Java code1 ALOAD02 DUP3 GETFIELD ViolentObject.x : I4 ICONST_15 IADD6 PUTFIELD ViolentObject.x : I

简单解释下
1 局部变量0位置的值压入栈,也就是x的引用,注意不是值.
2 复制栈顶一个字长,并压栈,也就是栈中此时有两个x的引用.
3 将当前栈顶的ref弹出,并将ref所引用的值压入栈.
4 把数字1压入栈
5 弹出栈顶两个字(x的值和1),并相加,把结果(值a)压入栈.
6 弹出栈顶(值a)和x的引用,并将(值a)赋给x的引用,栈空.

正因为一个x++,需要执行6条命令才完成,假设当线程1执行到5步的时候,暂停,线程2执行,x被加1,并返回,这时线程1又开始执行,将上一次的值又赋回去,也就是线程2的x++操作白做了.实际执行过程中,123456任意一步都可能暂停,情况就更复杂了.

终其原因就是因为x++并不是一条命令执行完成了,才导致y可能比x大.

[解决办法]
资源,竞争是很精辟的东西,有很多国外的书解释很精彩!

热点排行