---
title: 'Calling JNI code from multiple Java threads:  sharing state'
canonical: https://0110.be/posts/Calling_JNI_code_from_multiple_Java_threads%3A__sharing_state
markdown_url: https://0110.be/posts/Calling_JNI_code_from_multiple_Java_threads%3A__sharing_state.md
id: 481
published_at: '2021-06-01T00:00:00Z'
updated_at: '2025-11-29T14:32:05Z'
author: Joren
tags:
- name: Code
  markdown_url: https://0110.be/tags/Code.md
- name: UGent
  markdown_url: https://0110.be/tags/UGent.md
---

# Calling JNI code from multiple Java threads:  sharing state

<div style="float:right">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="301px" height="161px" viewBox="-0.5 -0.5 301 161" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2021-06-01T13:10:51.965Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.9.3 Chrome/80.0.3987.158 Electron/8.2.0 Safari/537.36&quot; etag=&quot;bNxISs8bfODPtaRI5J9W&quot; version=&quot;12.9.3&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;DG2niK77s8uMG9i-pMya&quot; name=&quot;Page-1&quot;&gt;3Vptk+IoEP41fjwrgHn7uM7s3t1u3dVWzVbd7kcmQUNtDFOIo96vPzBgQmAc9YxmzUxZoYEOPP2ku4GM0MNi8zvHL8VfLCflCAb5ZoQeRxCGMJa/SrCtBRMQ1II5p3ktAo3gif5LtNA0W9GcLK2GgrFS0BdbmLGqIpmwZJhztrabzVhpP/UFz4kjeMpw6Ur/obkoamlipqXkfxA6L8yTQZTWNQtsGuuZLAucs3VLhD6O0ANnTNR3i80DKRV2Bpe636c3avcD46QSx3SYiRh+2ZbVNBc05OWX5Odi/htKajWvuFzpGevRiq2BgLNVlROlBYzQdF1QQZ5ecKZq19LmUlaIRamrZ7QsH1jJ+K4vyjFJZpmULwVnP0mrJsoS8jyTNRlbUNniMVCtDEaqMC/xcqnv9SgJF2Tz5vzBHlXJRsIWRPCtbKI7wFQbQjMRJLq8buwKJlpWtG1qLIg1l+Z73Q3c8kYjfgr6cc/ohyTJJz70E/iMougQ+hdAHKDBIR6dAnhwMuCzJCOZl+7PSTgJu6zuE3w4scGHQTAOHfgTD/pJX+C7bAcu+gVbPK+WpyMfqj+vo9ldqgerREteX5cBWz7EgGu4nsYeuE3YaMMNoWl4ccABcBD/jF+xlHwrOMH50oFfQiBsnG08K1aRDvhahEs6r2Qxk5ARKZ8qQKkMpB90xYLmuXqM17D2S3cJ39OhP4g8vifwsL831wOgYwypaLr7lybAgtyxORAcnDnS90MBqfIPKodUOCq3rZy1bQzMRafFt4JWpuoTLU1TsqHie+v+h8J2HOrS40ZDvStsLdxJ7mSoHdTlmNmKZ+TAbOvJueZpwR960DcyTkos6Ks9Dp9J9BO+MipH2LyMcScWgY6Kevy6VzuH7ShyghoIus5T4j4nwlG148h+4ufTBrou1cObspTrk7derqEGMcdKKBjHnhjmS9jS3mKYgfdg1nAngHtTtGvj7abInhz5TrI0uSgcw5unadBNjNH9Qh6mt8Y7PALuO/EoEbi5Pxl+niXB5dvv7UKrlyo23XalPvIzs7e5y14ONIz89r9OHqcCVNBcaNJxpsEYTtLmstUfneOFgaP2ujme2bYaMGUvSL1wwIwKHdMfzSKU2qri4LocctcJx3HIRxybWue6K8OzYJymkcW1JH7HQcrCV8KpBEXtJVyagdGRvq9OE25FVRTYfgmhDqOOpSbqOrjuhmvf1HRXVEOh5o1c4NEEvGn0RXKxEsWt+Bp2aXSus0Sxk6VLZfDKURcNlpbKY1rMBAD9Cg7ztv6yc/r3P4K5h5+usr7pOTmJnnobPsfLYn9462MkGAfQJqUOxodoqUrn02vgawwncTt3rzgKDivqmzDuNsNFCHOmCxt2ZL0t5SYdyiVnUg4hMA5TZwX8doDum4JHfO7wa1PwdpRxD6KCc1kD39fVN1HcTbrPf/8pBVNOlQ26pLmbc2nY/UTJ2LWHc2lZbL72qy3XfDKJPv4H&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<defs/><g><rect x="160" y="40" width="140" height="120" rx="18" ry="18" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><rect x="0" y="40" width="140" height="120" rx="18" ry="18" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><rect x="110" y="60.5" width="80" height="80" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><path d="M 250 57.5 L 263.5 68.75 L 250 80 L 236.5 68.75 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 25px; height: 1px; padding-top: 69px; margin-left: 238px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
1

</div>
</div>
</div>
</foreignObject><text x="250" y="72" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">1</text>

</switch>
</g><rect x="10" y="20" width="100" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 30px; margin-left: 11px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
Java Threads

</div>
</div>
</div>
</foreignObject><text x="60" y="34" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Java Threads</text>

</switch>
</g><rect x="190" y="20" width="100" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 30px; margin-left: 191px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
C states

</div>
</div>
</div>
</foreignObject><text x="240" y="34" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">C states</text>

</switch>
</g><path d="M 66.37 70.28 L 103.63 70.47" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 61.12 70.26 L 68.13 67.96 L 66.37 70.28 L 68.11 72.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 108.88 70.49 L 101.86 73.96 L 103.63 70.47 L 101.9 66.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="50" cy="100.5" rx="10" ry="9.75" fill="#f5f5f5" stroke="#666666" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 101px; margin-left: 41px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
2

</div>
</div>
</div>
</foreignObject><text x="50" y="104" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">2</text>

</switch>
</g><ellipse cx="50" cy="70.25" rx="10" ry="9.75" fill="#f5f5f5" stroke="#666666" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 70px; margin-left: 41px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
1

</div>
</div>
</div>
</foreignObject><text x="50" y="74" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">1</text>

</switch>
</g><path d="M 250 89.25 L 263.5 100.5 L 250 111.75 L 236.5 100.5 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 25px; height: 1px; padding-top: 101px; margin-left: 238px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
2

</div>
</div>
</div>
</foreignObject><text x="250" y="104" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">2</text>

</switch>
</g><path d="M 250 119 L 263.5 130.25 L 250 141.5 L 236.5 130.25 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 25px; height: 1px; padding-top: 130px; margin-left: 238px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
3

</div>
</div>
</div>
</foreignObject><text x="250" y="134" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">3</text>

</switch>
</g><ellipse cx="50" cy="130.75" rx="10" ry="9.75" fill="#f5f5f5" stroke="#666666" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 131px; margin-left: 41px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
3

</div>
</div>
</div>
</foreignObject><text x="50" y="134" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">3</text>

</switch>
</g><path d="M 66.37 100.5 L 103.63 100.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 61.12 100.5 L 68.12 98.17 L 66.37 100.5 L 68.12 102.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 108.88 100.5 L 101.88 104 L 103.63 100.5 L 101.88 97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 66.37 130.65 L 102.63 130.1" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 61.12 130.73 L 68.08 128.29 L 66.37 130.65 L 68.15 132.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 107.88 130.02 L 100.94 133.62 L 102.63 130.1 L 100.83 126.62 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 196.05 130.47 L 230.13 130.28" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 190.8 130.49 L 197.78 126.96 L 196.05 130.47 L 197.82 133.96 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 235.38 130.26 L 228.4 133.79 L 230.13 130.28 L 228.36 126.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 196.37 100.5 L 230.13 100.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 191.12 100.5 L 198.12 97 L 196.37 100.5 L 198.12 104 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 235.38 100.5 L 228.38 104 L 230.13 100.5 L 228.38 97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 195.57 69.43 L 230.13 68.86" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 190.32 69.52 L 197.26 65.9 L 195.57 69.43 L 197.38 72.9 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 235.38 68.77 L 228.44 72.39 L 230.13 68.86 L 228.32 65.39 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 109 131 L 191.6 130.5" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 110 100.5 L 190 100.5" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 110 70.01 L 190 70.01" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><rect x="100" y="0" width="100" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)">

<switch>
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">

<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 10px; margin-left: 101px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
JNI Bridge

</div>
</div>
</div>
</foreignObject>

</switch>
</g></g></svg><br>

<caption>
<small>Mapping Java threads to C states in a JNI bridge</small>

</caption>
</div>
This post deals with the problem of using stateful C code from *multiple Java threads*. With JNI ([Java Native Interface](https://en.wikipedia.org/wiki/Java_Native_Interface)) it is possible to glue C code to a Java environment. There are [many helpful tutorials](https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html) on how to call C code and receive results. JNI helps to reuse existing, often highly complex and computationally expensive, C code.

The introductory tutorials often stop once it is made clear how to repackage (simple) datatypes and do not mention threads. It is, however, reasonable to expect JNI code to take into account thread-safety and proper multi-threading. In all but the simplest cases it is not that straightforward to share state at the C side and allow JNI code to be called from multiple Java threads. Incorrectly sharing state can lead to memory leaks and segmentation faults (segfaults) and crashes the application. In what follows, a way to share thread-local state is presented.

It is quite common to have an `init`, `work` and `dispose` method to create a state, use that state and do some work and finally dispose of used resources. Each Java thread independently calls these methods and expects results. These results should not change if multiple Java threads are calling the same methods. In other words: the state should remain Java thread-local. A typical Java class could look like the code below.

With the Java code in mind, the C code should know which Java thread is used and which state needs to be used for the work. Luckily there is a way to find out: [The JNI specification states that each `JNIEnv` is local to a Java thread](https://developer.ibm.com/languages/java/articles/j-jni/). So we can use the `JNIEnv` pointer to identify a thread. This is the idea that is used below.

The code maps a `JNIEnv` pointer to a structure with (any) state information. An unordered map is used for this mapping. There is, however, still a problem: multiple threads can call the init method at once. So multiple threads potentially write to the `unordered_map` at the same time which leads to problems. To prevent this from happening a mutex is used. The mutex, together with a [unique lock](https://www.cplusplus.com/reference/mutex/unique_lock/), makes sure that only a single thread writes to the unordered map. The same holds for the dispose method.

The work method does not need a unique lock since it does not write to the unordered map and reading from multiple threads is no problem.

<div style="width:100%" class="sixfour">
````c
#include <unordered_map>
#include <mutex>

const int DATA_ARRAY_SIZE = 300000 * 2;

struct BridgeState {
    jfloat *data;
};

//A hash map with a JNIEnv * as key and a BridgeState * as value
std::unordered_map<uintptr_t, uintptr_t> stateMap;

//A mutex to ensure that writes to the stateMap are synchronized.
std::mutex stateMutex;

JNIEXPORT jint JNICALL Java_init(JNIEnv *env, jobject object) {
    //Makes sure only one thread writes to the stateMap
    std::unique_lock<std::mutex> lck(stateMutex);

    BridgeState *state = new BridgeState();
    uintptr_t env_addresss = reinterpret_cast<uintptr_t>(env);

    state->data = new jfloat[DATA_ARRAY_SIZE];

    uintptr_t state_addresss = reinterpret_cast<uintptr_t>(state);
    stateMap[env_addresss] = state_addresss;

    return 1;
}

JNIEXPORT jint JNICALL Java_work(JNIEnv *env, jobject object) {
    //get a ref to the state pointer
    uintptr_t env_addresss = reinterpret_cast<uintptr_t>(env);
    BridgeState *state = reinterpret_cast<BridgeState *>(stateMap[env_addresss]);

    //do something with state->data, e.g. calculate the sum
    int sum = 0;
    for (int i = 0; i < DATA_ARRAY_SIZE; i++) {
        state->data[i] = state->data[i] + 1;
        sum += (int)state->data[i];
    }
    return sum;
}

JNIEXPORT jint JNICALL Java_dispose(JNIEnv *env, jobject object) {
    //Makes sure only one thread writes to the stateMap
    std::unique_lock<std::mutex> lck(stateMutex);

    uintptr_t env_addresss = reinterpret_cast<uintptr_t>(env);
    BridgeState *state = reinterpret_cast<BridgeState *>(stateMap[env_addresss]);
    stateMap.erase(env_addresss);

    //cleanup memory
    delete[] state->data;
    delete state;
    return 0;
}
````
</div>

This conceptual code has been lifted from a JNI library doing actual work: The [JGaborator JNI bridge](https://github.com/JorenSix/JGaborator/blob/master/gaborator/jgaborator.cc) . If you need more information on how to compile and use this construct in actual code, please have a look at the [JGaborator GitHub repository](https://github.com/JorenSix/JGaborator)

