`

为什么要进行数据同步

    博客分类:
  • java
阅读更多

转载:http://java.chinaitlab.com/line/779590.html

 

Java中的变量分为两类:局部变量和类变量。局部变量是指在方法内定义的变量,如在run方法中定义的变量。对于这些变量来说,并不存在线程之间共享的问题。因此,它们不需要进行数据同步。类变量是在类中定义的变量,作用域是整个类。这类变量可以被多个线程共享。因此,我们需要对这类变量进行数据同步。

    数据同步就是指在同一时间,只能由一个线程来访问被同步的类变量,当前线程访问完这些变量后,其他线程才能继续访问。这里说的访问是指有写操作的访问,如果所有访问类变量的线程都是读操作,一般是不需要数据同步的。

    那么如果不对共享的类变量进行数据同步,会发生什么情况呢?让我们先看看下面的代码会发生什么样的事情:

package test;

public class MyThread extends Thread
{
    public static int n = 0;

    public void run()
    {
        int m = n;
        yield();
        m++;
        n = m;
    }
    public static void main(String[] args) throws Exception
    {
        MyThread myThread = new MyThread ();
        Thread threads[] = new Thread[100];
        for (int i = 0; i < threads.length; i++)
            threads[i] = new Thread(myThread);
        for (int i = 0; i < threads.length; i++)
            threads[i].start();
        for (int i = 0; i < threads.length; i++)
            threads[i].join();
        System.out.println("n = " + MyThread.n);
    }
}

 

在执行上面代码的可能结果如下:

 

= 59

看到这个结果,可能很多读者会感到奇怪。这个程序明明是启动了100个线程,然后每个线程将静态变量n加1.最后使用join方法使这100个线程都运行完后,再输出这个n值。按正常来讲,结果应该是n = 100.可偏偏结果小于100.

    其实产生这种结果的罪魁祸首就是我们经常提到的“脏数据”。而run方法中的yield()语句就是产生“脏数据”的始作俑者(不加yield语句也可能会产生“脏数据”,但不会这么明显,只有将100改成更大的数,才会经常产生“脏数据”,在本例中调用yield就是为了放大“脏数据”的效果)。yield方法的作用是使线程暂停,也就是使调用yield方法的线程暂时放弃CPU资源,使CPU有机会来执行其他的线程。为了说明这个程序如何产生“脏数据”,我们假设只创建了两个线程:thread1和thread2.由于先调用了thread1的start方法,因此,thread1的run方法一般会先运行。当thread1的run方法运行到第一行(int m = n;)时,将n的值赋给m.当执行到第二行的yield方法后,thread1就会暂时停止执行,而当thread1暂停时,thread2获得了CPU资源后开始运行(之前thread2一直处于就绪状态),当thread2执行到第一行(int m = n;)时,由于thread1在执行到yield时n仍然是0,因此,thread2中的m获得的值也是0.这样就造成了thread1和thread2的m获得的都是0.在它们执行完yield方法后,都是从0开始加1,因此,无论谁先执行完,最后n的值都是1,只是这个n被thread1和thread2各赋了一遍值。这个过程如下图如示:

 

 

也许有人会问,如果只有n++,会产生“脏数据”吗?答案是肯定的。那么n++只是一条语句,又如何在执行过程中将CPU交给其他的线程呢?其实这只是表面现象,n++在被Java编译器编译成中间语言(也叫做字节码)后,并不是一条语言。让我们看看下面的Java代码将会被编译成什么样的Java中间语言。

    Java源代码

 

public void run()
{
    n
++;
}

被编译后的中间语言代码

 

  001  public void run()
  
002  {
  
003      aload_0         
  
004      dup             
  
005      getfield
  
006      iconst_1        
  
007      iadd            
  
008      putfield       
  
009      return          
  
010  }

大家可以看到在run方法中只有n++一条语句,而在编译后,却有7条中间语言语句。我们并不需要知道这些语句的功能是什么,只看一下第005、007和008行语句。在005行是getfield,根据它的英文含义可知是要得到某个值,因为这里只有一个n,所以毫无疑问,是要得到n的值。而在007行的iadd也不难猜测是将这个得到的n值加1.在008行的putfield的含义我想大家可能已经猜出来了,它负责将这个加1后的n再更新回类变量n.说到这,可能大家还有一个疑惑,执行n++时直接将n加1不就行了,为什么要如此费周折。其实这里涉及到一个Java内存模型的问题。

    Java的内存模型分为主存储区和工作存储区。主存储区保存了Java中所有的实例。也就是说,在我们使用new来建立一个对象后,这个对象及它内部的方法、变量等都保存在这一区域,在MyThread类中的n就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈,在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量,也就是方法变量。在线程要修改主存储区中的变量时,并不是直接修改这些变量,而是将它们先复制到当前线程的工作存储区,在修改完后,再将这个变量值覆盖主存储区的相应的变量值。

    在了解了Java的内存模型后,就不难理解为什么n++也不是原子操作了。它必须经过一个拷贝、加1和覆盖的过程。这个过程和在MyThread类中模拟的过程类似。大家可以想象,如果在执行到getfield时,thread1由于某种原因被中断,那么就会发生和MyThread类的执行结果类似的情况。要想彻底解决这个问题,就必须使用某种方法对n进行同步,也就是在同一时间只能有一个线程操作n,这也称为对n的原子操作。 

分享到:
评论

相关推荐

    java多个数据库实现数据同步

    7. **微服务架构**:在微服务中,每个服务通常维护自己的数据库,通过API Gateway或Service Mesh进行服务间的通信,实现数据同步。这种方法强调服务自治,但需要良好的服务间协调机制。 在实现过程中,需要注意以下...

    mysql-oracle数据同步

    当业务发展需要将MySQL中的数据迁移到Oracle时,数据同步技术就显得尤为重要。 数据同步可以分为实时同步和批量同步。实时同步通常通过触发器、消息队列或者中间件实现,确保数据一旦在源数据库更新,目标数据库...

    C#数据同步源代码

    这些框架和库使得开发者可以方便地读取、写入和更新数据,同时支持异步操作,为数据同步提供基础。 3. **异步编程**:在C#中,异步编程是实现高效数据同步的关键。通过`async/await`关键字,开发者可以在不阻塞主线...

    Informatica数据同步解决方案

    Informatica 数据同步解决方案 Informatica 数据同步解决方案是指通过 Informatica 平台实现的数据同步解决方案,该方案使您的 IT 组织能够以批量、接近实时和实时模式使用高质量数据,实现所有运营和交易系统的...

    使用Kettle同步mysql数据,增量同步教程执行步骤

    在IT行业中,数据同步是一项关键任务,特别是在大数据处理和企业级应用中。本文将深入探讨如何使用Kettle工具实现MySQL数据库之间的增量同步...在实践中,务必根据自己的实际情况进行调整,确保数据同步的安全和准确。

    SpringBoot定时任务实现Oracle和mysql数据同步

    Spring Boot作为Java领域的一个热门微服务框架,提供了强大的定时任务功能,能够帮助我们实现不同数据库间的数据同步,比如Oracle到MySQL。本篇文章将详细讲解如何利用Spring Boot的定时任务特性,结合Java的相关...

    数据同步,数据异地传输

    数据同步和异地传输是IT行业中确保数据安全和高可用性的重要策略。在信息化时代,数据的价值日益凸显,因此,有效地管理和保护数据变得至关重要。本文将深入探讨数据同步和异地传输的相关知识点。 首先,数据同步是...

    apache-camel-3.7.0_数据同步_

    在标题“apache-camel-3.7.0_数据同步_”中提到的“数据同步”,是指利用Apache Camel实现不同系统、数据库或应用程序间的数据交换和一致性维护。在描述中,“数据同步Data synchronization”进一步强调了这个功能的...

    mysql历史数据同步到clickhouse 已测试

    在需要对历史数据进行深度分析时,将MySQL的数据同步到ClickHouse可以显著提升查询效率。 **1. 数据同步方案** - **binlog同步**: 利用MySQL的二进制日志(binlog)进行实时数据同步。例如,可以使用`Maxwell`或`...

    数据同步技术白皮书 PDF

    ### 数据同步技术白皮书知识点概述 #### 一、数据同步技术的重要性 数据同步技术是现代信息技术领域中不可或缺的一部分,特别是在分布式系统环境中。随着互联网技术和云计算的发展,数据量的快速增长和分布式的应用...

    使用Kettle同步mysql数据,增量同步

    增量同步通常指的是只同步自上次同步以来发生改变的数据,而不是对整个数据库内容进行全量同步,这样能够节省网络带宽和计算资源,提高数据同步的效率。 在Kettle中,数据同步常见于以下四种应用场景: 1. 只增加...

    如何实现多个系统间的数据同步

    数据同步在IT行业中是一项至关重要的任务,特别是在分布式系统、微服务架构或...同时,随着技术的发展,如区块链、流计算等新技术也在为数据同步提供新的解决方案。在实践中不断探索和学习,是持续优化数据同步的关键。

    springboot+mysql+oracle+druid 双数据库进行数据同步

    在数据同步场景下,Druid可以帮助优化数据库连接,提高数据读写效率,同时其监控能力可以帮助我们实时查看数据同步的性能指标。 **流处理**:为了处理大量数据并防止内存溢出,项目采用了流处理方式。流处理允许...

    异步数据同步组件

    异步数据同步组件是一种在分布式信息系统中实现数据高效流通的技术,它确保了业务系统的完整性和数据传输的可靠性。这一组件涉及到多个关键领域,包括数据循环、分布式系统架构、异步数据传输、数据同步的四个阶段、...

    Oracle 12.2主从数据同步方法

    需要对这些异常情况进行排查和解决,以确保数据同步的正确性和可靠性。 Oracle 12.2 主从数据同步方法可以实现容器数据库的数据同步,目前只支持 DML 的数据同步,不包含 DDL 的同步。通过对 Oracle 12.2 主从数据...

    c#定时服务数据同步源代码

    "c#定时服务数据同步源代码"这个主题涉及到的核心知识点是利用C#编写一个定时服务,实现数据的自动化同步功能。这样的系统通常用于保持多个数据库或者应用程序间的数据一致性,尤其在分布式系统中尤为重要。 首先,...

    数据同步工具类,使用Rest接口同步数据

    这是一个利用java中的定时任务和jdbc去...1、在配置文件中synnum同步的数量必须要大于接口查询出来的数量。 2、在解析json数据的时候,如果同步记录数只有一条会同步不到,需要将其中的一个if记录数等于1的判断去掉。

    kettle全量多表数据同步

    在进行全量多表数据同步之前,首先需要建立一个资源库来存放所有的转换和作业。资源库是Kettle的一个核心概念,它用于存储和管理转换、作业和其他与数据处理相关的对象。 **1.1 添加资源库** 为了确保资源库能够...

Global site tag (gtag.js) - Google Analytics