diff --git a/native/android/app/src/cpp/CommMMKV.cpp b/native/android/app/src/cpp/CommMMKV.cpp
--- a/native/android/app/src/cpp/CommMMKV.cpp
+++ b/native/android/app/src/cpp/CommMMKV.cpp
@@ -14,6 +14,18 @@
     method(cls);
   }
 
+  static void lock() {
+    static const auto cls = javaClassStatic();
+    static auto method = cls->getStaticMethod<void()>("lock");
+    method(cls);
+  }
+
+  static void unlock() {
+    static const auto cls = javaClassStatic();
+    static auto method = cls->getStaticMethod<void()>("unlock");
+    method(cls);
+  }
+
   static void clearSensitiveData() {
     static const auto cls = javaClassStatic();
     static auto method = cls->getStaticMethod<void()>("clearSensitiveData");
@@ -93,6 +105,14 @@
       []() { CommMMKVJavaClass::initialize(); });
 }
 
+CommMMKV::ScopedCommMMKVLock::ScopedCommMMKVLock() {
+  NativeAndroidAccessProvider::runTask([]() { CommMMKVJavaClass::lock(); });
+}
+
+CommMMKV::ScopedCommMMKVLock::~ScopedCommMMKVLock() {
+  NativeAndroidAccessProvider::runTask([]() { CommMMKVJavaClass::unlock(); });
+}
+
 void CommMMKV::clearSensitiveData() {
   NativeAndroidAccessProvider::runTask(
       []() { CommMMKVJavaClass::clearSensitiveData(); });
diff --git a/native/android/app/src/main/java/app/comm/android/fbjni/CommMMKV.java b/native/android/app/src/main/java/app/comm/android/fbjni/CommMMKV.java
--- a/native/android/app/src/main/java/app/comm/android/fbjni/CommMMKV.java
+++ b/native/android/app/src/main/java/app/comm/android/fbjni/CommMMKV.java
@@ -75,6 +75,15 @@
     }
   }
 
+  public static void lock() {
+    initialize();
+    getMMKVInstance(mmkvIdentifier, mmkvEncryptionKey).lock();
+  }
+
+  public static void unlock() {
+    getMMKVInstance(mmkvIdentifier, mmkvEncryptionKey).unlock();
+  }
+
   public static void clearSensitiveData() {
     initialize();
     synchronized (mmkvEncryptionKey) {
diff --git a/native/cpp/CommonCpp/Tools/CommMMKV.h b/native/cpp/CommonCpp/Tools/CommMMKV.h
--- a/native/cpp/CommonCpp/Tools/CommMMKV.h
+++ b/native/cpp/CommonCpp/Tools/CommMMKV.h
@@ -31,5 +31,11 @@
   public:
     using std::runtime_error::runtime_error;
   };
+
+  class ScopedCommMMKVLock {
+  public:
+    ScopedCommMMKVLock();
+    ~ScopedCommMMKVLock();
+  };
 };
 } // namespace comm
diff --git a/native/ios/Comm/CommMMKV.mm b/native/ios/Comm/CommMMKV.mm
--- a/native/ios/Comm/CommMMKV.mm
+++ b/native/ios/Comm/CommMMKV.mm
@@ -6,6 +6,18 @@
 
 #import <Foundation/Foundation.h>
 #import <MMKV.h>
+#import <MMKVCore/MMKV.h>
+
+// Core MMKV C++ implementation and Android wrapper have public `lock` and
+// `unlock` methods while Obj-C wrapper doesn't. However Obj-C wrapper has
+// private instance variable of type `mmkv::MMKV *`. Interface redeclaration
+// below lets us access it. This pattern is hacky but it is well known in
+// Obj-C and used in our codebase in CommSecureStore.
+@interface MMKV () {
+@public
+  mmkv::MMKV *m_mmkv;
+}
+@end
 
 namespace comm {
 
@@ -29,6 +41,17 @@
   return mmkv;
 }
 
+CommMMKV::ScopedCommMMKVLock::ScopedCommMMKVLock() {
+  CommMMKV::initialize();
+  MMKV *mmkv = getMMKVInstance(mmkvIdentifier, mmkvEncryptionKey);
+  mmkv->m_mmkv->lock();
+}
+
+CommMMKV::ScopedCommMMKVLock::~ScopedCommMMKVLock() {
+  MMKV *mmkv = getMMKVInstance(mmkvIdentifier, mmkvEncryptionKey);
+  mmkv->m_mmkv->unlock();
+}
+
 void assignInitializationData() {
   std::string encryptionKey =
       crypto::Tools::generateRandomString(mmkvEncryptionKeySize);