문제

Let's say i have CPU with 2 cores. If i will run background processing service with Executors.newFixedThreadPool(4) threads am i correct that:

  1. during lifetime of executor service single thread could be running on different cores
  2. even with no synchronization if thread A runs its code on core 1 and left in CPU cache some value of shared singleton let's say, and then if thread B will run it's code on same core and will try get singleton value from same memory location which has representation left by thread A in cache - it will get it from CPU core L1 or L2 cache. And if thread B will use synchronization it will read new value from main memory(latest version). In general if some thread left in CPU core cache some value of private field of shared object - another thread that could be run on same core could see value of private member from cache left by other thread.
  3. If both options on top will be true - if L2 cache will be used to store shared between threads(which will add new values to map) HashMap instance and L2 will be a shared between all cores cache - does it mean that while skipping not atomic operations(if we want just to see correct/latest values in map) we can skip synchronization. For example will it be correct to have a HashMap and skip synchronization on reading existing values from Map:

Example

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Launcher {


public static void main(String[] args) throws Exception {
    final Stats stats = new Stats();
    final Random key = new Random();

    ExecutorService service = Executors.newFixedThreadPool(2);

    service.submit(new Runnable() {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                String keyValue = String.valueOf(key.nextInt(10));
                int value = stats.inc(keyValue);
                System.out.println("[A] Key " +  keyValue + " was incremented to " + value);
                try {
                    TimeUnit.MILLISECONDS.sleep(1500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    });

    service.submit(new Runnable() {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                int[] values = new int[10];
                for (int i = 0; i< 10; i++) {
                    values[i] = stats.get(String.valueOf(i));
                }

                System.out.println("[B] " + Arrays.toString(values));
                try {
                    TimeUnit.MILLISECONDS.sleep(1500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    });
}
static class Stats {

    private final Map<String, Number> statistics = new HashMap<String, Number>();

    public int inc(String key) {
        if (!statistics.containsKey(key)) {
            synchronized (statistics) {
                statistics.put(key, new AtomicInteger(0));
            }
        }

        return ((AtomicInteger) statistics.get(key)).getAndIncrement();
    }

    public int get(String key) {
        if (!statistics.containsKey(key)) {
            return 0;
        }
        return statistics.get(key).intValue();
    }
}
}

Could you point me to some valuable documentation of low level management of multithreaded code in java?

Guys i really understand that we should not rely on specific architecture/CPU/ etc. I'm just curious if probability of described points bigger than 0 :)

Thx in advance

도움이 되었습니까?

해결책

You shouldn't make any assumptions about threads seeing values modified by other threads unless you synchronize on the access or make the variables volatile.

Any other behaviour is unreliable and subject to change.

Remember that Java is running on the JVM, not directly on your processor, and has license to make a LOT of optimisations to your running code. So while a lot of the behaviour carries over you cannot rely upon it. Especially since as soon as you run on different architecture or under different conditions the exact same bytecode may be optimised differently.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top