javamultithreadinglambdacollectionssynchronized

Unexpect Behavior with Multiple Java Threads and TreeMap.put()


When running the following code:

import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;

public final class ThreadTest {
    int N = 4;
    Map<Integer, Proxy> proxy = Collections.synchronizedMap(new TreeMap<>());
    Proxy p;
    
    public static void main(String[] args) {
        new ThreadTest();
    }
    
    public ThreadTest() {
        Thread[] t = new Thread[N];
        for(int c=0;c<N;c++) {
            final int i = c;
            t[i] = new Thread(() -> {
                try {
                    System.out.println( "From thread: " + i );
                    p = new Proxy(i);
                    proxy.put(i, p);
                    p.init();
                } catch (Exception e){}
            });
            t[i].start();
        }
        for(int c=0;c<N;c++) {
            try {
                t[c].join();
            } catch (InterruptedException e){}
        }
    }
    
    public class Proxy {
        int index;
        
        public Proxy(int index) {
            this.index = index;
            System.out.println( "Proxy: " + index );
        }
        
        public void init() {
            System.out.println("Initializing " + index );
        }
    }
}

the printed indices from the method init() are not unique. The following is an example output:

From thread: 0
From thread: 1
From thread: 2
From thread: 3
Proxy: 0
Proxy: 3
Proxy: 2
Proxy: 1
Initializing 3
Initializing 3
Initializing 1
Initializing 2

Although I have synchronized the TreeMap with Collections.synchronizedMap(), the result is the same for an unsynchronized Map. This behavior seems quite odd considering it is just a put method and the index has changed completely. Could anybody please explain what is happening? Thanks.


Solution

  • Your problem is that your p variable is shared by all threads.

    From the prints in init, it shows that this code block that the p variable is written over by another thread before init is called

                    p = new Proxy(i);
                    proxy.put(key, p);
                    p.init();