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

原文地址:https://www.cnblogs.com/Ch1nYK/p/9073900.html

时间: 2024-10-21 02:01:07

java多线程---volatile的相关文章

java多线程 -- volatile 关键字 内存 可见性

内存可见性(Memory Visibility) 1 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象   状态后,其他线程能够看到发生的状态变化. 2 可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情.   我们可以通过同步来保证对象被安全地发布.除此之外我们也可以使用一种更加轻量级的 volatile 变量. vola

[Java多线程] volatile 关键字正确使用方法

volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性,即多线程环境中,使用 volatile 关键字的变量仅可以保证不同线程读取变量时,可以读到最新修改的变量值,但是修改变量值时,却不能保证线程安全(可能存在写值覆盖现象).以下测试代码,展示了使用volatile关键字的几种方式. 1 /** 2 * <b>volatile 关键字正确用法</b><br> 3 * @author Gaylen 4 * @version V1.1.0 5

Java多线程——volatile关键字、发布和逸出

1.volatile关键字 Java语言提供了一种稍弱的同步机制,即volatile变量.被volatile关键字修饰的变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在每次读取volatitle类型变量时总会返回最新的值,而不是从寄存器中获取. 加锁机制既然可以确保可见性又可以确保原子性,而volatile只能确保可见性. 2.发布和逸出 "发布(Publish)"一个对象指,使对象能够在当前作用域之外的代码中使用.如将指向该对象的引用保存到其他代码可以访问的地方,或者在某一

Java多线程volatile关键字

JVM中,分为JVM Client模式,JVM Server模式.两者区别并不大,但serve启动模式启动较慢,但一旦运行起来后,在hash和method call方面的效率,比Client模式效率高数十倍. 线程在读取数据时,首先从主内存中读取数据,保存在本地工作内存中,然后对数据进行修改,修改完毕后,写入本地工作内存中,再从工作内存写入主内存.示例图如下: JVM如果以Server模式启动时,为了线程的效率,线程在第一次读取数据后,将数据保存在工作内存中,之后读取数据时,均从工作内存中读取.

java 多线程-volatile写后立即读

volatile线程对变量进行修改后,立刻写回到主内存线程对变量读取的时候,从主内存中读取,而不是缓冲,避免了指令重排 无法破除循环 public class my { private volatile static int num=0; public static void main(String[]args) throws InterruptedException { new Thread(()->{ while(num==0) { } }).start(); Thread.sleep(10

Java多线程(三)volatile域

相关文章 Java多线程(一)线程定义.状态和属性 Java多线程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源码分析 前言 有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步访问提供了免锁的机制.如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的.再讲到volatile关键字之前我们需要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见性和有序性

java多线程关键字volatile的使用

java多线程关键字volatile的作用是表示多个线程对这个变量共享. 如果是只读的就可以直接用,写数据的时候要注意同步问题. 例子: package com.ming.thread.volatiletesttrhead1; /** * volatile 关键字的使用 * volatile 这个关键字的作用就是保持由此关键字修饰的变量在多个线程之间可以看得见 * @author mingge * */ public class volatiletesttrhead extends Thread

Java多线程编程——volatile关键字

(本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThread extends Thread{ private boolean isRunning = true; // volatile private boolean isRunning = true; public boolean isRunning() { return isRunning; } public

JAVA多线程基础学习三:volatile关键字

Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个关键字的作用就是告诉编译器,只要是被此关键字修饰的变量都是易变的.不稳定的.那为什么是易变的呢?因为volatile所修饰的变量是直接存在于主内存中的,线程对变量的操作也是直接反映在主内存中,所以说其是易变的. 一.Java内存模型 Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理