Java原子操作小案例(线程,并发)

分析以下案例:

public class Test06 {
	public static void main(String[] args) {
		MyThread run=new MyThread();
		for(int i=0;i<5;i++){
			new Thread(run).start();
		}
		//等待线程执行完毕
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(run.count);
	}
}
class MyThread implements Runnable{
        //volatile保证count的可见性
	volatile int count =0;
	@Override
	public synchronized void run() {
		for(int i=0;i<1000;i++){
			count++;
		}
	}	
}

思考:输出的count的值会是多少呢?

分几种情况:

  1. count属性没有加volatile关键字修饰,方法没有加synchronized修饰。

        此种情况我们知道在多线程访问下,没有加任何的处理,最后结果一定是小于等于5000

  1. count属性加了volatile关键字修饰,方法没有加snnchronized修饰。

        此种情况count对于每一个线程都是可见的,但是方法没有进行加锁处理,同一时刻,多个线程可能读到同一个值进行添加,所以count的值也可能小于等于5000

  1. count属性加了volatile关键字修饰,方法加了synchronized修饰。

        此种情况,count对于每一个线程都是可见的,count++操作又有synchronized修饰,结果肯定是5000。答案正确吗?不是绝对正确的。为什么呢?

        解释:因为++操作不是原子操作,可能会存在指令重排,在高并发的情况下,计算机为了提高性能,就会进行指令重排,就可能出现问题,不能得到想要的答案。

       那么我们现在思考:这个问题要怎么解决呢?

java8的java.util.concurrent包中给我们提供了新的方法:

public class Test {

	public static void main(String[] args) {
		MyThread2 run = new MyThread2();
		for(int i=0;i<5;i++){
			new Thread(run).start();
		}
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(run.count.get());//
	}
}

class MyThread2 implements Runnable{
	//AtomicInteger通过CAS原理实现的
	volatile AtomicInteger count=new AtomicInteger(0);
	@Override
	public void run() {
		for(int i=0;i<1000;i++){
			count.getAndIncrement();//对count++操作
		}
	}	
}

使用此种方式可以保证所得到的结果一定是5000。如果对CAS感兴趣的可以进一步深入的了解。

猜你喜欢

转载自blog.csdn.net/ToBe_Coder/article/details/81587335
今日推荐