System.out,println对多线程的影响,以及主内存与工作内存的同步

先说一下之前对System.out.println的误会先举个例子
package com.yigjn.Thread;

public class MyThread extends Thread {
	private int count = 0;
	
	@Override
	public void run() {
		 for (int i = 0; i < 10000; i++) {  
            count++;
            System.out.println(Thread.currentThread().getName() + "=====");
		}
	}

	public static void main(String[] args) throws InterruptedException {
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread, "t1");
		Thread t2 = new Thread(myThread, "t2");
		Thread t3 = new Thread(myThread, "t3");
		Thread t4 = new Thread(myThread, "t4");
		Thread t5 = new Thread(myThread, "t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		while(Thread.activeCount() > 1) {
			Thread.yield();
		}
		System.out.println(myThread.count);
	}
}

通过这段代码会发现count会保证有序性,保证了线程安全,但是去掉run方法循环体中输出语句会发现,这时候的count++非原子性操作在多线程的并发下就会变成线程不安全,之前网上有很多人说因为println是线程安全

 public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

但是又觉得不太合理这个解释,因为sysnchronized锁的只是输出的对象,所以经过查阅资料,发现hotsport

 jvm有一个锁优化原则那就是 : 粗化锁

            如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即    使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(膨胀)到整个操作序列的外部(由多次加锁编程只加锁一次)。

所以实际run方法里面执行的代码是

synchronized(){
  for(;;){
  }
}

所以这才保证了上面的代码是线程安全的。


工作内存与主内存的同步

    大家也都知道jvm大致分为工作内存与主内存

    说到工作内存与主内存的同步操作就得说到一个关键次 就是:volatile

        volatile是java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的

    

package com.yigjn.Thread;

public class MyThread extends Thread {
	private boolean flag = true; 
	
	@Override
	public void run() {
		 while(flag) {
//			 try {
//				 Thread.sleep(10);
//			 } catch (InterruptedException e) {
//				 // TODO Auto-generated catch block
//				 e.printStackTrace();
//			 }
			 System.out.println(Thread.currentThread().getName() + "======");
		 }
	}

	public static void main(String[] args) throws InterruptedException {
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread, "t1");
		t1.start();
		Thread.sleep(3000);
		myThread.flag = false;
		Thread.sleep(3000);
	}
}
   之前看有人说上述代码,可以保证flag可以在被修改之后获取到主内存的值,因此说是pringln是具有清空工作内存,同步主内存的功能,但其实你会发现把注释代码松开输出语句注释掉,其实也是可以停止的,jvm在有volatile关键字时不存在工作内存中存储主内存的副本,来保证多线程的可见性,但是没有的时候,jvm也会尽可能的在空闲的时候会去同步主内存中的值,才造成的假象

你可能感兴趣的:(java,Thread)