zoukankan      html  css  js  c++  java
  • 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();
    		}
    	}
    }
    
  • 相关阅读:
    Js高程:教你如何写出可维护的代码
    ES6 尾调用和尾递归
    js判断对象是否为空对象,判断对象中是否有某属性
    WebSocket断开原因、心跳机制防止自动断开连接
    Js闭包使用姿势指南
    浏览器事件模型
    Js数组排序
    简单的前端错误处理
    移动端做弹窗类页面注意事项
    加载时常统计
  • 原文地址:https://www.cnblogs.com/Ch1nYK/p/9073900.html
Copyright © 2011-2022 走看看