java多线程---volatile

被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。
当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

  • 1.非volatile变量,读写时,每个线程从内存copy到cpu缓存中,多核cpu,每个线程在不同的cpu上被处理。变量会被copy到不同的cpu缓存中
  • 2.volatile变量,每次从内存中读,跳过了cpu缓存这一步。

  • 特性
    1.可见性。可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
    2.有序性。禁止指令重排序优化。

eg

public class VolatileTest {
	
	public static volatile int  i = 0 ;
	
	public static class VTTest implements Runnable{
			@Override
			public void run() {
				for (int j = 0; j < 1000; j++) {
					i++;	//可以理解为3步,1:读到工作内存,2:进行+1计算 3:写入新的i+1
					System.out.println(Thread.currentThread().getId() + ":" + i);
				}
			}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread[] t = new Thread[10];
		for (int i = 0; i < 10; i++) {
			t[i] = new Thread(new VTTest());
			t[i].start();
		}
		for (int i = 0; i < 10; i++) {
			t[i].join();
		}
	}
}
由于volatile保证了可见性,那么在每个线程中对i自增完之后,在其他线程中都能看到修改后的值啊,所以有10个线程分别进行了1000次操作,那么最终i的值应该是1000*10=10000。这里面就有一个误区了,volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。比如现在有2个线程,线程1开始读取i到工作内存,进行+1操作,在线程1还没有进行写操作,线程2也开始读取i到工作内存,进行+1操作,由于线程1还没有写操作,所以线程2的i还是有效的,线程1将递增的结果写到主内存,线程2读取到工作内存中的i无效,但是线程2已经对i递增完,也开始对i进行写入到主内存操作,所以最终2个线程最终只对i进行一次递增操作。那么怎么保证原子性呢,答案是加锁,使用synchronized或lock。

lock---eg

public class VolatileTest {
	
	public static volatile int  i = 0 ;
	
	public static ReentrantLock lock = new ReentrantLock();  
	
	public static class VTTest implements Runnable{
			@Override
			public void run() {
				for (int j = 0; j < 1000; j++) {
					lock.lock();
					i++;	//i = i + 1;非原子操作
					System.out.println(Thread.currentThread().getId() + ":" + i);
					lock.unlock();
				}
			}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread[] t = new Thread[10];
		for (int i = 0; i < 10; i++) {
			t[i] = new Thread(new VTTest());
			t[i].start();
		}
		for (int i = 0; i < 10; i++) {
			t[i].join();
		}
	}
}

synchronized---eg

public class VolatileTest {
	
	public static volatile int  i = 0 ;
	
	public static class VTTest implements Runnable{
			@Override
			public void run() {
				increase();
			}
	}
	
	public synchronized static void increase(){
		for (int j = 0; j < 1000; j++) {
			i++;	//i = i + 1;非原子操作
			System.out.println(Thread.currentThread().getId() + ":" + i);
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread[] t = new Thread[10];
		for (int i = 0; i < 10; i++) {
			t[i] = new Thread(new VTTest());
			t[i].start();
		}
		for (int i = 0; i < 10; i++) {
			t[i].join();
		}
	}
}
posted @ 2018-05-22 20:29  Ch1nYK  阅读(187)  评论(0编辑  收藏  举报