千家信息网

Java并发编程中Volatile不能保证数据同步

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇文章将为大家详细讲解有关Java并发编程中Volatile不能保证数据同步,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。通过一个实例去验证vola
千家信息网最后更新 2025年01月18日Java并发编程中Volatile不能保证数据同步

这篇文章将为大家详细讲解有关Java并发编程中Volatile不能保证数据同步,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

通过一个实例去验证volatile修饰的变量并不能保证其数据同步。

Java内存模型规定了所有变量都存储在主内存中,每条线程都有自己的工作内存,线程的工作内存保存了被该线程使用到变量的主内存副本拷贝,线程 对变量的所有操作(读取,赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程也不能直接访问对方工作内存中的变量,线程间变量值的 传递均需要通过主内存来完成,线程,主内存,工作内存三者的交互关系如图所示。



当一个变量定义成volatile之后, 保证了此变量对所有线程的可见性,也就是说当一条线程修改了这个变量的值,新的值对于其它线程来说是可以立即得知的.此时,该变量的读写操作直接在主内存中完成.

Volatile 变量具有 synchronized 的可见性特性但是不具备原子特性

Volatile variables share the visibility features of synchronized, but none of the atomicity features.

虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。

While the increment operation (x++) may look like a single operation, it is really a compound read-modify-write sequence of operations that must execute atomically -- and volatile does not provide the necessary atomicity.

在多线程并发的环境下, 各个线程的读/写操作可能有重叠现象, 在这个时候, volatile并不能保证数据同步.

下面将给出一个实例:

实例 ==> 500个线程一起运行,每个线程对1到100求和1000次操作,然后将一个volatile共享变量值加1. 当500个线程都完成操作之后, 期望的值是500,因为每个线程执行完毕之后都会对这个volatile变量加1.

一直循环执行这个程序,直到出现volatile变量的值小于500为止,也就是出现数据不同步。

public class NonSafeThread implements Runnable {      /** 共享资源, 每个线程执行完之后加 1 */     private volatile int volatileCount = 0;      public void run() {          /*          * 每个线程调用sum100()方法,1000次          */          for (int i = 1; i <= 1000; i++) {             sum100();         }          /*          * 计算完毕之后, volatileCount 加 1          */          increase();     }          private void increase()     {         volatileCount++;     }      /**      * 对 1 到 100 求和      */     private int sum100() {         int result = 0;         for (int i = 1; i <= 100; i++) {             result += i;         }         return result;     }      /**      * @return the volatileCount      */     public int getVolatileCount() {         return volatileCount;     }  }
/**  * @author Eric  *   * @version 1.0  */  public class NonSafeThreadTest {      public static void main(String[] args) {          /** 记录循环次数 */         int loopCount = 0;          /** 以main函数主线程创建一个是线程组 */         ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();          for (;;) {             loopCount++;              /*              * 启动500个线程,初始化的线程会添加到当前线程组中              */             NonSafeThread nonSafeThread = new NonSafeThread();             startThreads(nonSafeThread);              /*              * 如果线程组中除了主线程之外,还有其它线程,则休眠5毫秒,然后再判断线程组中 剩余的线程数,直到只剩下主线程一个为止。              */             while (!isOnlyMainThreadLeft(threadGroup)) {                 sleep(5);             }              /*              * 500个线程运行完毕,那么此时的volatile变量volatileCount的值应该500, 因为每个线程将其值加1。              *               * 验证是否出现线程不安全的情况。              */             validate(loopCount, nonSafeThread.getVolatileCount(), 500);         }     }      /**      * 启动500个线程      */     private static void startThreads(NonSafeThread nonSafeThread) {          for (int i = 0; i < 500; i++) {             new Thread(nonSafeThread).start();         }     }      /**      * 验证是否出现线程不安全的情况。 如果是,则打印出线程不安全的信息。      */     private static void validate(int loopCount, int actualValue,             int expectedValue) {         if (!isVolatileCountExpected(actualValue, expectedValue)) {             printNonSafeMessage(loopCount, actualValue, expectedValue);             /*              * 正常退出程序。              */             System.exit(0);         }     }      /**      * 在控制台打印出现线程不安全时的信息。      */     private static void printNonSafeMessage(int loopCount, int actualValue,             int expectedValue) {         System.out.println(String.format(                 "第%d次循环,出现线程不安全的情况,volatile的值不正确,期望值是%d, 但是500个线程运行的情况下是%d",                 loopCount, expectedValue, actualValue));     }      /**      * 判断实际中的volatile值与期望值是否一致。      */     private static boolean isVolatileCountExpected(int actualValue,             int expectedValue) {         return actualValue == expectedValue;     }      /**      * 让线程休眠millis毫秒      */     private static void sleep(long millis) {         try {             Thread.sleep(millis);         } catch (InterruptedException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }     }      /**      * 判断一个线程组是否只剩下主线程了。      *       * 如果是则返回true,如果不是则放回false.      */     private static boolean isOnlyMainThreadLeft(ThreadGroup tg) {         return tg.activeCount() == 1;     }  }

某次运行,输出的结果如下:

第83次循环,出现线程不安全的情况,volatile的值不正确,期望值是500, 但是500个线程运行的情况下是499

在这种情况下,可以通过 Lcak和synchronized来保证数据的同步。

如:

1. 使用Lock,修改NonSafeThread类的run方法的内容:

public void run() {          lock.lock();          try {             /*              * 每个线程调用sum100()方法,1000次              */              for (int i = 1; i <= 1000; i++) {                 sum100();             }              /*              * 计算完毕之后, volatileCount 加 1              */              increase();                      } finally {             lock.unlock();         }      }

2. 使用synchronized

public void run() {          synchronized ("") {             /*              * 每个线程调用sum100()方法,1000次              */              for (int i = 1; i <= 1000; i++) {                 sum100();             }              /*              * 计算完毕之后, volatileCount 加 1              */              increase();         }     }

如果用Lock或者synchronized修改了NonSafeThread类, 如果再想跑这个程序的话,需要控制一下NonSafeThreadTest中for循环中执行的次数,比如1000次 (我运行程序的时候,一般都在100次以内打印出数据不安全的结果),以免导致程序在Lock或者synchronized修改后一直执行下去.

关于Java并发编程中Volatile不能保证数据同步就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

线程 变量 内存 数据 情况 保证 同步 安全 运行 程序 工作 循环 方法 期望 内容 原子 实例 期望值 特性 验证 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 云南正规软件开发零售价格 福建软件开发设施服务标准 南港网络技术有限公司 精易阿里妈妈采集软件开发 湖南企业软件开发商 数据库郑明秋 php服务器入门教程视频学习 数据库有日期的数据类型 广东省网络技术大赛 威海同和软件开发有限 临沂市盛茂网络技术服务有限公司 戴尔服务器配置管理口地址 云服务器添加安全组 计算机网络技术招生口号 基于企业网的服务器架设论文 软件开发公司的人员有哪些 甘肃展厅多媒体软件开发 网络技术专员岗位 数据库分布式怎么解决 世界分布式数据库排名 关于网络安全的主题手抄报 网络安全培训机构需要什么资质 课程设计网络安全攻击与防御 介绍服务器虚拟化的支撑技术 南昌影墨互联网科技有限公司 广轻计算机网络技术专业就业 原装进口戴尔服务器批发零售价 pubmed数据库中字段标识 陇南粘构网络技术有限公司 温州数字化智慧医院软件开发
0