java多线程---volatile

Posted Ch1nYK

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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进行一次递增操作。那么怎么保证原子性呢,答案是加锁。

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();
        }
    }
}



以上是关于java多线程---volatile的主要内容,如果未能解决你的问题,请参考以下文章

多线程的异步死循环和volatile关键字

Java多线程的volatile底层实现原理

Java多线程volatile域

Java中volatile关键字及其作用是什么?

多线程之:正确使用 Volatile 变量

java多线程之volatile讲解