Java多线程升级篇(二)——volatile关键字

本文深入探讨Java多线程中的volatile关键字,解释其确保数据可见性而非原子性的特点。通过示例代码展示volatile在并发场景中的局限性,并对比了volatile与synchronized的区别,强调在需要原子性操作时应选择synchronized。
摘要由CSDN通过智能技术生成

Java多线程升级篇(二)——volatile关键字

这篇的内容全部是关于另外一个关键字volatile的。在介绍具体内容之前还有一点内容要补充一下。就是关于java运行的一些内容。

关于java运行的一些内容:

Java在运行线程的时候,会有两个堆栈,一个是线程的私有堆栈,一个是公共堆栈。有时在运行的时候,为了提高线程的效率,比如JVM的server模式,会从私有堆栈中取得数据,而不会从公有堆栈取得数据。在一定程度上会造成很多的不确定性,如对一个变量的值的变更命名已经进行,但是放入公共堆栈以后,却一直不从堆栈中取得,这样就会造成很大的问题,而这次介绍的volatile关键字的作用就是强制让线程从公共堆栈中取得数据。

Volatile的使用

public class New_runnable implements Runnable{
	volatile private boolean isContinuePrint=true;
	public boolean isContinuePrint() {
		return this.isContinuePrint;
	}
	public void setContinuePrint(Boolean isC) {
		this.isContinuePrint=isC;
	}
	public void printStringMethod() {
		try{
			while(this.isContinuePrint==true) {
				System.out.println(Thread.currentThread().getName());
				Thread.sleep(1);
			}
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	public void run(){
		this.printStringMethod();
	}
}
public class Test_thread{
	public static void main(String[] args){
		try {
			New_runnable printStringService=new New_runnable();
			new Thread(printStringService).start();
			Thread.sleep(10);
			System.out.println("Let it down!");
			printStringService.setContinuePrint(false);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
}

运行结果:
在这里插入图片描述
Volatile关键字的作用是强制从公共内存中读取变量的值,增加了实例多个线程之间的可见性。但是volatile关键字最致命的缺点是不支持原子性。
Volatile关键字和synchronized关键字相比各有优劣

  1. volatile是线程同步的轻量级实现,性能优于synchronized,但是volatile只能用于修饰变量,而synchronized可以用于修饰方法以及代码块。但是在开发中使用synchronized关键字的作用还是很大的;
  2. 多线程访问volatile不会发生阻塞,而synchronized一定会发生阻塞
  3. Volatile可以保证数据的可见性,但是不能保证原子性,但是synchronized可以保证原子性,也可以间接的保持可见性,因为它会将私有内存和公共内存中的数据同步;
  4. Volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。

Volatile关键字非原子特性

public class New_thread extends Thread{
	volatile public static int count;
	private static void addCount() {
		for(int i=0;i<100;i++) {
			count++;
		}
		System.out.println(count);
	}
	public void run() {
		this.addCount();
	}
}
public class Test_thread{
	public static void main(String[] args){
		New_thread[] test=new New_thread[100];
		for(int i=0;i<100;i++) {
			test[i]=new New_thread();
		}
		for(int i=0;i<100;i++) {
			test[i].start();
		}
	}
}

运行结果:
在这里插入图片描述
结果应该是10000的,但是没有达到10000,由此可见volatile是不具有原子性的。
更改代码如下:

public class New_thread extends Thread{
	volatile public static int count;
	synchronized private static void addCount() {
		for(int i=0;i<100;i++) {
			count++;
		}
		System.out.println(count);
	}
	public void run() {
		this.addCount();
	}
}

运行结果为:
在这里插入图片描述
这个结果是正确的。在这种情况下,volatile关键字的使用反倒被synchronized替代,所以,volatile在这种情况下是多余的。
Volatile主要使用的场合是在多个线程可以感知到实例变量的更改,并且可以获得最新的使用值。Volatile并不处理数据的原子性,而是强制让数据的读写影响到主内存。也就是说,volatile关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量还是需要加锁同步的。

Synchronized代码块有volatile同步的功能

我们又回到了synchronized关键字,为什么呢?因为这个关键字也具有将线程工作内存中的私有变量与公共内存中的变量同步的功能。
如下列代码:

public class LoginClass {
	private boolean isContinueRun=true;
	public void runMethod() {
		while(isContinueRun==true) {
			
		}
		System.out.println("Stop!");
	}
	public void stopMethod() {
		isContinueRun=false;
	}
}
public class Another_thread extends Thread{
	private LoginClass object;
	public Another_thread(LoginClass a) {
		this.object=a;
	}
	public void run() {
		this.object.stopMethod();
	}
}
public class New_thread extends Thread{
	private LoginClass object;
	public New_thread(LoginClass a) {
		this.object=a;
	}
	public void run() {
		this.object.runMethod();
	}
}
public class Test_thread{
	public static void main(String[] args){
		try {
			LoginClass test=new LoginClass();
			New_thread t1=new New_thread(test);
			t1.start();
			Thread.sleep(1000);
			Another_thread t2=new Another_thread(test);
			t2.start();
			System.out.println("Stop command has sended");
		
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
}

运行结果:
在这里插入图片描述
原因很简单了,因为各线程之间的数据没有可视性造成的。这就造成了程序无法终结的结果。这个时候使用volatile关键字当然可以,结果就是程序终止。但是这次我们使用synchronized关键字来实现。
代码如下:

public class LoginClass {
	private boolean isContinueRun=true;
	public void runMethod() {
		String anything ="";
		while(isContinueRun==true) {
			synchronized(anything) {
			}
		}
		System.out.println("Stop!");
	}
	public void stopMethod() {
		isContinueRun=false;
	}
}

关键字synchronized可以保证在同一时刻,只有一个线程可以执行一个方法或者代码块。它包含有两个特性:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改状态。

至此关于volatile关键字的内容就总结完毕,当初面试****公司的时候,就没有回答出来这个问题,还需要继续努力。
下一篇是关于线程间通信的问题中的等待/通知机制,顺带着会实现生产者消费者的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值