关于java的volatile问题

这段代码为什么会输出a:1,b:3呢, 我加了volatile关键字,不是一个线程修改了值,对另一个线程可见吗 ?

 public class Test4 {

    //加了volatile,为什么会出现: a:1,b:3

    volatile long a = 1;
    volatile long b = 2;


    public void change(){
        a = 3;
        b = a;
    }

    public void print(){
        System.out.println("a:"+a+",b:"+b);
    }

    public static void main(String[] args) {
        while (true){
            final Test4 test4 = new Test4();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(10);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    test4.change();
                }
            }).start();


            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(10);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    test4.print();
                }
            }).start();

        }
    }
}

volatile 只能保证单个变量的修改是原子的
a = 3;
b = a;
这样分两行写,还是需要同步的。

volatile能够保证对变量的操作是原子的,但是 a = 3, b = a;两个赋值操作不是原子的,在给a赋值3的时候,b 可能已经拿到了a 之前的值

 a = 3;
b = a;

给这两个加上同步也会有a:1,b:3,,或者a:1,b:2,,加上同步锁,,不影响第二个线程,,第二个线程不访问change()方法

问题在这里,,连个线程,,一个改连个数,,一个读取两个数(太乱了,)
有a:1,b:3,,或者a:3,b:2,,等是必然现象啊

原因如下:
【1】线程运行时随时可能发生调度,
【2】当第一个线程刚刚修改完两个数时,,第二个线程进去读,,读到的结果就是 a:3 b:3
【3】当第一个线程刚刚初始化修改完a时还未修改,发生调度,,,第二个线程进去读,,读到的结果就是 a:3 b:2(几率比较小,还是有的。)
.....剩下情况同理

最后分析一下为什么会出现【a:1,b:3】
System.out.println("a:"+a+",b:"+b);。内部是实现其实也是一个字符一个字符写到buffer中,然后刷到控制台

初始化玩a,b【a=1,b=2】,第二个线程开始输出方法 当写完【"a:"+a】时,,调度了,,还有【",b:"+b】没来得及写
第一个线程运行,修改完a和b,,【a=3,b=3】,,修改完,第二个线程开始运行了,,继续刚才的输出,,写【",b:"+b】,,此时b等于3
结果就会出现【a:1,b:3】这种情况。

有问题还可以追问,,以上仅是个人观点,,如有错误请指出【嘿嘿,共同探讨】。