Java多线程Sample 1

来源:互联网 发布:Linux设置用户得密码 编辑:程序博客网 时间:2024/06/11 11:04

源码地址在github的ThreadsSafeProblem仓库demo1包。

一、问

内存中有一个整型变量value=0,两个线程都获取它后,将变量自加,会输出什么结果?

二、代码

UnsafeSequence.java

package demo1;public class UnsafeSequence {    private int value;    public int getNextVal(){        return value++;    }}

MyRunnable.java

package demo1;public class MyRunnable implements Runnable {    UnsafeSequence unsafeSequence;    //向线程注入公用对象    public MyRunnable(UnsafeSequence unsafeSequence) {        this.unsafeSequence = unsafeSequence;    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName()+":"+unsafeSequence.getNextVal());    }}

Main.java

package demo1;public class Main {    public static void main(String[] args) {        //定义公用对象        UnsafeSequence unsafeSequence = new UnsafeSequence();        //定义两个线程        MyRunnable m1 = new MyRunnable(unsafeSequence);        MyRunnable m2 = new MyRunnable(unsafeSequence);        Thread t1 = new Thread(m1);        Thread t2 = new Thread(m2);        //启动线程        t1.start();        t2.start();    }}

三、选项

看完代码后,你觉得会输出什么呢?

  • A. t1:0 和 t2:1
  • B. t1:0 和 t2:0
  • C. t1:1 和 t2:0
  • D. t2:0 和 t1:1
  • E. t2:0 和 t1:0
  • F. t2:1 和 t1:0

答案:ABCDEF

四、分析原因

UnsafeSequence类中的value++操作,执行的过程中,大概可以分为取数、操作、写回3个操作,由于两个线程的这三个操作顺序的不确定性,所以上面程序输出的结果也有所不同。

Created with Raphaël 2.1.0取出valuevalue++写回value

如图这种状况,输出结果为t1:0 t2:0,至于其它情况可以多允许几次,估计就能看到不同的结果。
这里写图片描述

五、通过字节码分析

使用javap -v UnsafeSequence.class反编译查看字节码:

{  public demo1.UnsafeSequence();    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: invokespecial #10                 // Method java/lang/Object."<init>":()V         4: return      LineNumberTable:        line 3: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       5     0  this   Ldemo1/UnsafeSequence;  public int getNextVal();    flags: ACC_PUBLIC    Code:      stack=4, locals=1, args_size=1         0: aload_0         1: dup         2: getfield      #18                 // Field value:I         5: dup_x1         6: iconst_1         7: iadd         8: putfield      #18                 // Field value:I        11: ireturn      LineNumberTable:        line 7: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0      12     0  this   Ldemo1/UnsafeSequence;}

可以很清楚的看到,getNextVal方法做的0-11的操作,如果t1的putfield执行在t2的aload_0后,则t1/t2输出的结果都为0,否则一个为0一个为1。

0 0