并发编程4-volatile

30 篇文章 0 订阅

1.概述

  • Volatile称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的。

  • 可见:一个线程修改了这个变量的值,在另外一个线程中能够读到这个修改后的值。也就是读写可见.

  • volatile不保证原子性

  • Synchronized除了线程之间互斥意外,还有一个非常大的作用,就是保证可见性

2.示例

2.1 多线程set和get结果不一致

/**
 *
 * 多线程一个线程set,一个线程get,会造成set的结果在get中看不到.
 */
public class VolatileThread1_NotVolatile {

    public int value = 1;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.value = value;
    }


    /**
      为了保证setValue后执行,所以sleep了20ms
     */
    public static void main(String[] args) throws InterruptedException {

        VolatileThread1_NotVolatile volatileThread1_NotVolatile = new VolatileThread1_NotVolatile();

        new Thread(new Runnable() {
            @Override
            public void run() {
                volatileThread1_NotVolatile.setValue(10);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("get线程得到的结果为:" + volatileThread1_NotVolatile.getValue());
            }
        }).start();

        Thread.sleep(1000);

        System.out.println("最终的结果为:" + volatileThread1_NotVolatile.getValue());
    }
}

2.2 通过synchronized不能一定解决set get同步问题

/**
 * 保证可见性的前提:  多个线程拿到的是同一把锁,否则是保证不了的。
 * 多线程一个线程set,一个线程get,会造成set的结果在get中看不到.
 * <p>
 * 通过synchronized能同步实例,实现set结果能同步
 */
public class VolatileThread2_Synchronized {

    public int value = 1;

    public synchronized int getValue() {
        return value;
    }

    public synchronized void setValue(int value) {
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.value = value;
    }

    /**

     由于synchronized
     1)可能执行完主线程的get(),然后才进入锁实例进行set(),就是1
     2)可能先锁实例set(),然后才执行主线程的get(),那就是10
     注:synchronized只是能阻止两个线程同时执行set()或get(),不能左右线程的顺序.

     根据原因是没有锁属性value,而通过volatile可以实现属性可见性.
     */
    public static void main(String[] args) {

        VolatileThread2_Synchronized volatileThread2_Synchronized = new VolatileThread2_Synchronized();

        new Thread(new Runnable() {
            @Override
            public void run() {
                volatileThread2_Synchronized.setValue(10);
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run()  {
                System.out.println("get线程的结果为:" +volatileThread2_Synchronized.getValue());
            }
        }).start();

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终的结果为:" + volatileThread2_Synchronized.getValue());


    }

}

2.3 通过volatile来解决属性(变量)的线程间可见

public class VolatileThread3_Volatile {

    //保证变量在多个线程的值的一致性
    public volatile int value = 1;

    /**
      新线程的结果为:10
     最终的结果为:10
     */
    public static void main(String[] args) {
        VolatileThread3_Volatile volatileThread3_volatile = new VolatileThread3_Volatile();

        //保证set值先执行,然后另一个线程能获取到结果
         volatileThread3_volatile.value = 10;

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程的结果为:"+volatileThread3_volatile.value);
            }
        }).start();

        System.out.println("最终的结果为:" + volatileThread3_volatile.value);
    }
}


2.4 volatile的使用场景


/**
 * volatile的使用场景
 * 任务1多次之后才改变值,
 * 任务2一直判断任务1的值,当任务1一改变值,任务2就得到任务1的结果了并使用.
 */
public class VolatileThread4_Volatile {

	public volatile boolean run = false;


	/**
	 执行了第 1 次
	 执行了第 2 次
	 执行了第 3 次
	 执行了第 4 次
	 执行了第 5 次 
	 线程2执行了...
	 */
	public static void main(String[] args) {
		VolatileThread4_Volatile volatileThread4_volatile = new VolatileThread4_Volatile();

		new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 1;i<=5;i++) {
					System.out.println("执行了第 " + i + " 次");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				volatileThread4_volatile.run = true;
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while(!volatileThread4_volatile.run) {
					// 不执行
				}
				System.out.println("线程2执行了...");
			}
		}).start();
	}
}


2.5 synchronized替代volatile实现volatile的使用场景

synchronized替代volatile实现volatile的使用场景,但是volatile有轻量化,并简洁.以下使用VolatileThead4实现VolatileThead3同样的功能.

**
 * 用synchronized来替代VolatileThead3的volatile的使用场景
 * 任务1多次之后才改变值,
 * 任务2和3一直判断任务1的值,当任务1一改变值,任务2就得到任务1的结果了并使用.
 */
public class VolatileThread5_Synchronized {

	public  boolean run = false;

	public synchronized boolean isRun() {
		return run;
	}

	public synchronized void  setRun(boolean run) {
		this.run = run;
	}


	/**
	 执行了第 1 次
	 执行了第 2 次
	 执行了第 3 次
	 执行了第 4 次
	 执行了第 5 次
	 线程3最终执行了...
	 */
	public static void main(String[] args) {

		VolatileThread5_Synchronized volatileThread5_synchronized = new VolatileThread5_Synchronized();

		new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 1;i<=5;i++) {
					System.out.println("执行了第 " + i + " 次");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//d.run = true;
				volatileThread5_synchronized.setRun(true);
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while(!volatileThread5_synchronized.run) {
					// 不执行
					//System.out.println("线程2这就执行了");
				}
				System.out.println("线程2最终执行了...");
			}
		}).start();


		new Thread(new Runnable() {
			@Override
			public void run() {
				while(!volatileThread5_synchronized.isRun()) {
					// 不执行
					//System.out.println("线程3这就执行了");
				}
				System.out.println("线程3最终执行了...");
			}
		}).start();

	}
}

3. volatile底层原理

  • Lock指令

    • 在多处理器的系统上,将当前处理器缓存行(cpu缓存的最小单位)的内容写回到系统内存,
    • 这个写回到内存的操作会使在其他CPU里缓存了该内存地址的数据失效(所以volatile实现一个线程改变,让其它线程得到的当前内容失效,并且将当期线程的结果回写)
  • 存储等级 硬盘 --> 内存 --> CPU的缓存(L1,L2等)

  • 多线程在多cpu处理数据时,会将内存中的数据缓存到处理器缓存中,而在cpu处理完之后而不是立刻回写到内存中去.

    那么在某个cpu中处理的数据,在另一个cpu中就是不可见的.所以如果需要同步内容,就加volatile,内容立刻回写并让其它的失效.

  • 如果大量使用volatile,则会造成大量处理器的缓存失效(减少CPU的性能优化),使用会影响性能. 所以要根据具体场景选择使用.

  • volatile还有影响指令重排序.

    不得不提的volatile及指令重排序(happen-before)

4.synchronized和volatile

  • synchronized 保证数据的原子性操作

  • volatile 只是保证可见性,不保证数据的原子性(比如如果getA()中包含有a++则volatile使用无意义),保证原子性的可见性

  • synchronized能取代volatile,但是volatile不能取代synchronized,但是volatile有轻量化,并简洁.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值