Android10 Binder原理概述深入解析

目录
  • IPC工具介绍
    • Pipe
    • Sign
    • message queue
    • shared memory
    • Socket
  • AIDL
  • HIDL

IPC工具介绍

Binder作为Android 众多的IPC通讯手段之一,在Framework的数据传输中起到极为关键的作用。为什么Google需要重新创造Binder这么一个IPC工具,使用linux默认提供的Pipe、Socket、共享内存、信号、消息队列等IPC工具不行吗?

答案是 这些传统的linux IPC工具有一部分android也在使用,只是在某些场合下它们无法满足需求,所以才创造了Binder这么一个工具。 为了更好地向各位读者解释为什么需要Binder,我们先来简单地认识一下linux传统的IPC工具,让大家对它们的优势和劣势有一个更为直观的认识。

Pipe

管道是一种最基本的IPC工具,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。它有如下特质:

  • 其本质是一个伪文件(实为内核缓冲区)
  • 由两个文件描述符引用,一个表示读端,一个表示写端。
  • 规定数据从管道的写端流入管道,从读端流出,一般只能单向通信,双向通信需建立两个管道。
  • 只能用于父子、兄弟进程(有共同祖先)间通信。
  • 数据一旦被读走,便不在管道中存在,不可反复读取。 因此,管道的局限性表现得非常明显,它并不适合一对多的方式建立通讯(尽管技术上能够实现),原因在于第5条,管道中的数据无法反复读取。类似的还有FIFO(命名管道),它在管道的基础上做了升级,摆脱了第4条的共同祖先的限制,但仍要面临一对多通讯的困境。

framework中有没有使用Pipe进行通讯?答案是有,但是用的很少,相比之下用的更多的是FIFO!!各位读者如果感兴趣的话,可以在源码中搜一下 TransferPipe这个类,在其中可以找到Pipe的痕迹。

Sign

信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。linux系统已经预置了一部分信号标识,它们都有着特殊的含义,部分信号如下所示:

  • SIGHUP:本信号在用户终端结束时发出,通常是在终端的控制进程结束时,通知同一会话期内的各个作业,这时他们与控制终端不在关联。比如,登录Linux时,系统会自动分配给登录用户一个控制终端,在这个终端运行的所有程序,包括前台和后台进程组,一般都属于同一个会话。当用户退出时,所有进程组都将收到该信号,这个信号的默认操作是终止进程。此外对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
  • SIGINT:程序终止信号。当用户按下CRTL+C时通知前台进程组终止进程。
  • SIGQUIT:Ctrl+\控制,进程收到该信号退出时会产生core文件,类似于程序错误信号。
  • SIGILL:执行了非法指令。通常是因为可执行文件本身出现错误,或者数据段、堆栈溢出时也有可能产生这个信号。
  • SIGTRAP:由断点指令或其他陷进指令产生,由调试器使用。
  • SIGABRT:调用abort函数产生,将会使程序非正常结束。
  • SIGBUS:非法地址。包括内存地址对齐出错。比如访问一个4个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法地址的非法访问触发。
  • SIGFPE:发生致命的算术运算错误。
  • SIGKILL:用来立即结束程序的运行。不能被捕捉、阻塞或忽略,只能执行默认动作。

信号只能起到对进程的通知作用,它无法发送复杂的数据类型,不适合用于进程间的数据交换。

信号在整个framework中也扮演了极为重要的角色,各位读者可以通过搜索sigemptysetsigaddset等关键字,在源码中找到它们的身影。

message queue

消息队列,Unix的通信机制之一,可以理解为是一个存放消息(数据)容器。将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序。消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护。

其中数字 1 表示类型为 1 的消息,数字2、3、4 类似。彩色块表示消息数据,它们被挂在对应类型的链表上。

消息队列的缺陷在于:容量受到系统限制;消息队列的发送方与接收方没有强关联性,容易造成发送方往消息队列中存放了消息,没有接收方来取消息或接收方没有及时取消息的问题,消息的及时性无法保障。

目前在Android 10的非内核源码范围内,没有发现使用消息队列。

shared memory

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

共享内存利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的,因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决,开发上手难度较高,容易出错,且存在数据安全隐患。

目前在Android 10的非内核源码范围内,没有发现使用共享内存。

Socket

Socket这个不需要特别介绍了,不管是做C++开发还是Java开发,都会涉及到套接字编程。相对其他的IPC方式,Socket是最适合做一对多这种通讯需求的。它的问题在于数据需要经过两次拷贝,通讯效率相对低下。这个问题在电脑等设备上都不是什么特别大的问题,但考虑到Android搭载的移动设备,尤其是早期的移动设备,这个问题就很致命了。

framework中当然也存在Socket的使用痕迹,比如 system/core/init/init.cpp这个文件中就采用epoll机制,实现init进程与其子进程的通讯。

Android更看重的是效率和一对多通讯的问题,无法采用传统的IPC工具实现,所以只能考虑自己另起炉灶。除此之外,传统的IPC无法获得对方进程的PID\UID,从而无法鉴别对象的身份,从而会使Android系统的安全性无法得到保证(ps:无法获得对方进程的身份指的是Linux默认没有提供获取通讯进程的身份的接口,并不是说采用传统IPC没有办法实现这样的安全管控需求,只是谷歌在综合考虑了上述所有的因素的情况下,在共享内存的基础上做了一套新的解决方案)。

这里给各位读者留个思考题,有兴趣的读者可以自己动手去实验一下:

在一对多通讯的场景下,Binder的传输效率一定会比Socket高吗?(提示:Socket包括BIO、NIO、NIO2、epoll等,请不要局限在BIO的通讯方式)

AIDL

AIDL 是 Android interface definition Language 的英文缩写, 意思Android 接口定义语言,它与Binder有着千丝万缕的联系。

AIDL是谷歌使用Java 编程语言的语法定义的专门服务于Binder IPC通讯的脚本语言,推出的根本原因是为了避免Binder通讯中大量模板代码的书写。AIDL脚本会在编译期间,由Android SDK 工具生成基于该 .aidl 文件的 IBinder 接口,并将其保存到项目的 generated/ 目录中。

我们来看一个简单的aidl文件:

packageackage com.example.commonservice;
// Declare any non-default types here with import statements
interface ITtsService {
   void showTts(in String uid,in int textId,in boolean toPlayTts,in int type);
   boolean isShowing();
}

它生成的java文件如下所示:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.commonservice;
// Declare any non-default types here with import statements
public interface ITtsService extends android.os.IInterface
{
  /** Default implementation for ITtsService. */
  public static class Default implements com.example.commonservice.ITtsService
  {
    @Override public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException
    {
    }
    @Override public boolean isShowing() throws android.os.RemoteException
    {
      return false;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.commonservice.ITtsService
  {
    private static final java.lang.String DESCRIPTOR = "com.example.commonservice.ITtsService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.commonservice.ITtsService interface,
     * generating a proxy if needed.
     */
    public static com.example.commonservice.ITtsService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.commonservice.ITtsService))) {
        return ((com.example.commonservice.ITtsService)iin);
      }
      return new com.example.commonservice.ITtsService.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_showTts:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _arg1;
          _arg1 = data.readInt();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          int _arg3;
          _arg3 = data.readInt();
          this.showTts(_arg0, _arg1, _arg2, _arg3);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_isShowing:
        {
          data.enforceInterface(descriptor);
          boolean _result = this.isShowing();
          reply.writeNoException();
          reply.writeInt(((_result)?(1):(0)));
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.commonservice.ITtsService
    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      @Override public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(uid);
          _data.writeInt(textId);
          _data.writeInt(((toPlayTts)?(1):(0)));
          _data.writeInt(type);
          boolean _status = mRemote.transact(Stub.TRANSACTION_showTts, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().showTts(uid, textId, toPlayTts, type);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public boolean isShowing() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        boolean _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_isShowing, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().isShowing();
          }
          _reply.readException();
          _result = (0!=_reply.readInt());
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.commonservice.ITtsService sDefaultImpl;
    }
    static final int TRANSACTION_showTts = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_isShowing = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.commonservice.ITtsService impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.commonservice.ITtsService getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException;
  public boolean isShowing() throws android.os.RemoteException;
}

虽然是简短的一个aidl文件,但生成的模板代码却极为复杂,为了整理清楚这段代码的结构,笔者先隐藏其部分内容:

public interface ITtsService extends android.os.IInterface
{
    /** Default implementation for ITtsService. */
    public static class Default implements com.example.commonservice.ITtsService
    {
    }
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.commonservice.ITtsService
    {
        private static class Proxy implements com.example.commonservice.ITtsService
        {
        }
        static final int TRANSACTION_showTts = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_isShowing = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException;
    public boolean isShowing() throws android.os.RemoteException;
}

可以看到,代码的结构如同套娃一样,一层接一层。各位读者不妨思考一下,如果不这样套娃,把ITtsService里的DefaultStub类移到外面来,这样做可不可以?

答案是,可以的,不过类的命名方式可能要稍微做一下修改,如ITtsService_DefalutITtsService_Stub,以便于引用上的区分。当然,代码的结构不是重点,虽然谷歌的方式可读性会差一点,但开发人员不需要直接和这些源码打交道,也不是不可以接受。

Binder不一定都是跨进程通讯,同样也支持同进程通讯,比如 Activity绑定Service,通过Binder实现数据传输。在同一进程通讯的情况下,Stub类身兼两职,因其implements了ITtsService,它可以作为客户端的调用方;同时,它也是服务端的实现方。而在跨进程通讯的情况下,则由Proxy来担任客户端的调用方。

至于Default这个类的作用,暂时不明,无法找到相关的资料得知为什么谷歌要生成这么一个类。

在此,AIDL的介绍先告一段落,其中更多的细节将放到后续的文章中再做补充。

HIDL

HIDL的生命周期及其短暂,它从Android 8引入,然后在Android 10 立马被 Stable AIDL 所取代,虽然没啥存在感,但还是简单地提及一下吧。

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是指用于在可以独立编译的代码库之间进行通信的系统。

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。

更多HIDL相关的资料,可以参考 source.android.google.cn/docs/core/a…

以上就是Android10 Binder原理概述深入解析的详细内容,更多关于Android10 Binder原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android Binder进程间通信工具AIDL使用示例深入分析

    目录 前言 AIDL AIDL示例 客户端 运行日志 AIDL通信过程分析 bindService流程分析 前言 众所周知,Android进程间通信采用的是Binder机制.Binder是Android系统 独有的进程间通信方式,它是采用mmp函数将进程的用户空间与内核空间的一块内存区域进行映射,免去了一次数据拷贝,相比Linux上的传统IPC具有高效.安全的优点.本文结合AIDL与bindService函数,在Android体系的应用层和Framework层,对Binder通信进行深入剖析,以

  • 浅谈Android IPC机制之Binder的工作机制

    进程和线程的关系 按照操作系统中的描述,线程是CPU调度的最小单位,同时线程也是一种有限的系统资源.而进程一般是指一个执行单元,在pc端或者移动端上是指一个程序或者一个应用.一个进程中可以包含一个或者是多个线程.所以他们的关系应该是包含和被包含的关系. 跨进程的种类 在Android中跨进程通信的方式有很多种,Bundle,文件共享,AIDL,Messenger,ContentProvider,Socket,这些都能实现进程间之间的通信,当然,虽然都能够实现进程间通信,但是他们之间的实现原理或者

  • Android 图文详解Binder进程通信底层原理

    之前了解到进程与多进程,涉及多进程不可避免的遇到了进程间通信,说到进程间通信,Binder 成了一道绕不过的坎.接下来咱们逐一了解.

  • Android中关于Binder常见面试问题小结

    目录 1.简单介绍下binder 2.Binder的定向制导,如何找到目标Binder,唤起进程或者线程? 3.Binder中的红黑树,为什么会有两棵binder_ref红黑树 4.Binder一次拷贝原理 5.Binder传输数据的大小限制? 6.系统服务与bindService等启动的服务的区别 7.Binder多线程 8.Android APP进程天生支持Binder通信的原理是什么? 9.同一个线程的请求必定是顺序执行,即使是异步请求(oneway) 1.简单介绍下binder bind

  • Android Binder的原理与使用

    前言 Binder是安卓中实现IPC(进程间通信的)常用手段,四大组件之间的跨进程通信也是利用Binder实现的,Binder是学习四大组件工作原理的的一个重要基础. 好多文章都会深入C代码去介绍Binder的工作流程,没点水平真的难以理解,本文不会太深入底层去剖析原理,尽可能较为简单的让大家了解Binder是怎么工作的. Binder的使用 在介绍Binder原理之前,我们先来看看在安卓中怎么使用Binder来进程间通信. 在使用之前我们先来介绍Binder的几个方法: public fina

  • Android中Binder IPC机制介绍

    目录 前言 一.Binder是什么? 二.为什么要使用Binder 三.IPC机制原理 传统IPC机制如何实现跨进程通信 Binder IPC机制原理 小结 前言 记得刚开始做Andorid那会,面试时最怕被问到Binder,就感觉战战兢兢不知道从什么地方说起,导致后来一直有一种恐惧感.当然现在没有这种感觉了,但是这块知识点一直模模糊糊的,最近在学Andorid framework课程,借此机会简单总结下其中Binder相关知识点. 一.Binder是什么? Binder是Android中一种进

  • Android中的binder机制详解

    前言 Binder做为Android中核心机制,对于理解Android系统是必不可少的,关于binder的文章也有很多,但是每次看总感觉看的不是很懂,到底什么才是binder机制?为什么要使用binder机制?binder机制又是怎样运行的呢?这些问题只是了解binder机制是不够的,需要从Android的整体系统出发来分析,在我找了很多资料后,真正的弄懂了binder机制,相信看完这篇文章大家也可以弄懂binder机制. 1.Binder是什么? 要理解binder,先要知道IPC,Inter

  • Android Binder 通信原理图文详解

    目录 前言 1. Binder的作用 2. 进程与Binder驱动如何通信 3. ServiceManager进程的作用 Binder Client.Binder Server.ServiceManager关系 ServiceManager注册进Binder 4. 进程添加服务到ServiceManager的流程 其它进程找到SM 添加服务到ServiceManager BBinder作用 5. 进程从ServiceManager获取服务的流程 其它进程找到SM 从ServiceManager获

  • Android10 Binder原理概述深入解析

    目录 IPC工具介绍 Pipe Sign message queue shared memory Socket AIDL HIDL IPC工具介绍 Binder作为Android 众多的IPC通讯手段之一,在Framework的数据传输中起到极为关键的作用.为什么Google需要重新创造Binder这么一个IPC工具,使用linux默认提供的Pipe.Socket.共享内存.信号.消息队列等IPC工具不行吗? 答案是 这些传统的linux IPC工具有一部分android也在使用,只是在某些场合

  • mybatis原理概述入门教程

    本文我将要介绍一下mybatis的框架原理,以及mybatis的入门程序,实现用户的增删改查,她有什么优缺点以及mybatis和hibernate之间存在着怎么样的关系,希望对小伙伴们有帮助,不足之处,还请多多指教. 什么是mybatis? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .2013年11月迁移到Github. MyBatis 是支持定

  • Java方法重载Overload原理及使用解析

    这篇文章主要介绍了Java方法重载Overload原理及使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 为什么要用方法重载: 对于功能类似的方法来说,因为参数列表不一样,如果定义不同名称的方法,太麻烦且难以记忆. 为了解决这个问题,引入方法的重载. 重载的定义: 多个方法的名称一样,但参数列表不一样. 不使用方法重载 定义三个功能类似的方法 public class TestOverload { public static int su

  • Pandas数据离散化原理及实例解析

    这篇文章主要介绍了Pandas数据离散化原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 为什么要离散化 连续属性离散化的目的是为了简化数据结构,数据离散化技术可以用来减少给定连续属性值的个数.离散化方法经常作为数据挖掘的工具 扔掉一些信息,可以让模型更健壮,泛化能力更强 什么是数据的离散化 连续属性的离散化就是在连续属性的值域上,将值域划分为若干个离散的区间,最后用不同的符号或整数 值代表落在每个子区间中的属性值 分箱 案例 1.

  • Java图形界面Swing原理及用法解析

    这篇文章主要介绍了Java图形界面Swing原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 JButton组件 布局管理器 FlowLayout 流式布局 BorderLayout 方位布局 GridLayout 表格布局 绝对布局 JLable 组件 文本框组件 JPanel轻量级容器 创建事件监听类 (更换监听类实现监听) 窗口监听适配器 都可使用匿名类实现监听 每个监听方法都可以返回一个Event对象来返回监听值 以上就是本

  • java阻塞队列实现原理及实例解析

    这篇文章主要介绍了java阻塞队列实现原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 阻塞队列与普通队列的不同在于.当队列是空的时候,从队列中获取元素的操作将会被阻塞,或者当队列满时,往队列里面添加元素将会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素.同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完

  • Java HashMap原理及实例解析

    这篇文章主要介绍了Java HashMap原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 示例 1 : HashMap的键值对 HashMap储存数据的方式是-- 键值对 package collection; import java.util.HashMap; public class TestCollection { public static void main(String[] args) { HashMap<String

  • Python partial函数原理及用法解析

    这篇文章主要介绍了Python partial函数原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 介绍 partial其实是Python模块functools中定义的一个函数,当我们需要经常调用某个函数时,但是其中某些参数是已知的固定值,这样可能会让代码显得冗余,这个时候就可以考虑使用partial函数. 使用 假设我们要做二进制转十进制 int('1000000', base=2) # 64 int('1010101', bas

  • Python assert关键字原理及实例解析

    这篇文章主要介绍了Python assert关键字原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Python assert(断言)用于判断一个表达式,在表达式条件为 False 的时候触发异常. 断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况. 语法格式: assert expression 等价于: if not expression: raise AssertionError asser

  • Java内存模型原子性原理及实例解析

    这篇文章主要介绍了Java内存模型原子性原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文就具体来讲讲JMM是如何保证共享变量访问的原子性的. 原子性问题 原子性是指:一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行. 下面就是一段会出现原子性问题的代码: public class AtomicProblem { private static Logger logger = LoggerFactory.

随机推荐