Java线程---一定要了解的Volatile关键字

Java线程---Volatile最轻量的同步机制

Volatile

接上一篇文章,volatile问题更多在于能否代替synchronized锁,答案是不行的。

想要对比两者关系可以看回上一篇文章 链接: Java线程 synchronized加锁(不能忽视的锁对象).

定义

volatile 具有可见性,保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立刻可见的,但是不能保证原子性,即不能保证在多线程中同时写的一个过程安全。

若是不加volatile关键字,涉及到JDK内部实现、内存模型等问题,其他线程就未必看得见。


Volatile的可见性

上代码看吧,不加volatile的情况(代码思路会放到注释中):

import cn.HYQ.WSQ.XY.Volatile;
/**
* 类说明:演示Volatile的提供的可能性
*/
public class VolatileCase{
	private static bollean ready;	//声明定义两个类变量
	private static int number;

	//定义一个线程类
	private static class PrintThread extends Thread{
		//重写run方法
		@Override
		public void run{
			System.out.println("PrintThread is runing...");
			while(!ready);	//设置无限循环,只有当ready等于true的时候停止
			System.out.println("number = " + number);
		}
	}
	
	public static void main(String[] args){
		Thread thread = new PrintThread();
		thread.start();		//启动开始一个线程
		SleepTools.second(1);
		number = 51;	//不加同步机制,子线程没办法看到主线程中对成员变量或者静态变量ready的改变
		ready = true;
		SleepTools.second(5);	//主线程休眠5s
		System.out.println("main is ended");
		
	}
}

来看看结果:
在这里插入图片描述
很明显的等到主线程结束了子线程都没有输出number 的值,说明 while 到主线程结束都没有停止不限循环,说明 ready 在主线程的改变子线程没有收到消息,所以一直都是 true

改正方法:在类成员对象变量前加上 volatile,保证他的可见性:

public class VolatileCase {
    private volatile static boolean ready;
    private static int number;
    ...
}

再来看看结果:
在这里插入图片描述
So

不加 volatile 时,子线程无法感知主线程修改了 ready 的值,从而不会退出循环, 而加了 volatile 后,子线程可以感知主线程修改了 ready 的值,迅速退出循环。
但是 volatile 不能保证数据在多个线程下同时写时的线程安全。


volatile的安全性

定义count成员变量并执行 ++,通过两个线程分别执行incCount方法10000次:

package cn.enjoyedu.ch1.vola;

/**
 * 类说明:
 */
public class NotSafe {
    privatev volatile long count =0;		//定义count

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

    //count进行累加
    public void incCount(){
        count++;
    }

    //定义线程
    private static class Count extends Thread{

        private NotSafe simplOper;

        public Count(NotSafe simplOper) {
            this.simplOper = simplOper;
        }

		//重写run方法
        @Override
        public void run() {
            for(int i=0;i<10000;i++){
                simplOper.incCount();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        NotSafe simplOper = new NotSafe();
        
        //启动两个线程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();
        
        Thread.sleep(50);
        System.out.println(simplOper.count);//20000?
    }

}

经过两个线程分别执行run方法,累加过后count的值是20000
当没有加上volatile关键字,所以两个线程在对count操作的时候看不到另一个线程同时的执行的结果,所以结果一定大概率不会是20000:
在这里插入图片描述
试多几次都不会是20000

现在加上volatile:

public class NotSafe {
    private volatile long count =0;

    public long getCount() {
        return count;
    }
    ...
}

再来看结果:
在这里插入图片描述
结果还是不行,就说明了在volatile在安全性上一个很重要的点:
只能保证可见,不保证原子!!!

综上所述,volatile 最适用的场景:一个线程写,多个线程读。
即既然多线程同时写不能保证安全性,那就一个人写,充分利用其可见性,别的线程可读。

《volatile的应用场景后续再补充》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值