(JAVA)对volatile和synchronized以及join()的理解和总结

一直对volatile和synchronized的理解都很模糊,今天终于写了个demo帮助自己理解。


都知道volatile可以保证多线程对一个对象的可见性,对于volatile修饰的对象或者一个变量,线程去读取的时候每次都会到主内存去读取,对变量的更新也会flush到主内存。这就保证了,这个变量对于每个线程都是一致的,不会存在由于cpu缓存存在而导致的不一致的情况。用了一个demo来帮助自己理解。



//对象
public class Student {


	public volatile int age = 0;
	
	
}
//线程类
public class TestThreadA implements Runnable{


	Student student1 ;
	
	public TestThreadA(Student student) {
		student1= student;
	}
	
	@Override
	public void run() {
		


			student1.age++;//对age+1操作


		System.out.println(Thread.currentThread().getName()+"*****"+student1.age);
	}
}
//启动类
public class Main {


	public static void main(String[] args) throws InterruptedException {
		Student student = new Student();
		for(int i = 0;i<50;++i){
			Thread aThread = new Thread(new TestThreadA(student));
			aThread.setName("Thread*******"+i);
			aThread.start();
		}
		System.out.println(student.age);
	}
}


在Student类变量age未加volatile修饰时:

     

50个线程对age=0进行+1操作,预期的结果应该是50,由于不同线程栈中age缓存的不一致,导致最终结果不正确。当给Student类中age加volatile修饰时:


运行多次发现,结果依然会有不为50的出现.
原来volatile只保证可见性但不保证原子性,Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。例如age++时,线程1先从主内存读取age=1,此时让出了CPU执行权,线程2获得执行执行,也读取到age=1,继续age++,然后写入主内存,主内存中age=2,此时线程1重新获得了执行,age++,结果为2,写入主内存,主内存中结果仍然为2。所以一般在多线程中使用volatile变量,为了安全,对变量的写入操作不能依赖当前变量的值:如age++或者age=age*2这些操作。


那么synchornized呢?
synchornized既保证可见性又保证原子性。


对Student类变量age去掉volatile修饰。TestThreadA类中的student1.age++用synchornized:

synchronized (student1) {
			student1.age++;
}

结果:


此时结果正确

synchronized会对对象加锁,同一时刻只有一个线程能访问该对象,也就是synchornized修饰的代码块,只能一个一个的线程同步执行,不能并发执行。那么volatile就不会造成线程的阻塞;而synchronized可能会造成线程的阻塞,也可能会造成死锁。

查看运行结果可以发现,main线程总是早于其他线程先结束,最终结果并不是程序的最终结果50。
join()方法就可以保证程序的最终结果50。a.join();就是让当前线程将cpu执行权交给a线程,直到a运行结束。


TestThreadA类中的student1.age++中的synchronized修饰去除。

Main类改为:

public class Main {

	public static void main(String[] args) throws InterruptedException {
		Student student = new Student();
		for(int i = 0;i<50;++i){
			Thread aThread = new Thread(new TestThreadA(student));
			aThread.setName("Thread*******"+i);
			aThread.start();
			aThread.join();
		}
			

		System.out.println(student.age);
	}
	
}

此时运行结果:

相当于一个一个线程顺序执行


参考:VOLATILE 只保证可见性,并不保证原子性

全面理解Java内存模型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值