Java线程与CPU核心的关系

时间:2014-01-17 22:48:32

标签: java multithreading multiprocessing cpu cpu-architecture

假设我拥有2核的CPU。如果我将使用Executors.newFixedThreadPool(4)线程运行后台处理服务我是否正确:

  1. 在执行程序服务的生命周期内,单个线程可以在不同的核心上运行
  2. 即使没有同步,如果线程A在核心1上运行其代码并在CPU缓存中保留一些共享单例的值,那么如果线程B将在同一核心上运行它的代码并尝试从同一内存获取单例值在缓存中由线程A留下表示的位置 - 它将从CPU核心L1或L2缓存中获取它。如果线程B将使用同步,它将从主存储器读取新值(最新版本)。一般情况下,如果CPU核心中的某些线程缓存了共享对象的私有字段的某些值 - 另一个可以在同一核心上运行的线程可以看到其他线程从缓存中保留私有成员的值。
  3. 如果顶部的两个选项都为真 - 如果L2缓存将用于存储线程之间的共享(这将为映射添加新值)HashMap实例和L2将在所有核心缓存之间共享 - 这是否意味着跳过非原子操作(如果我们只想在地图中看到正确/最新的值),我们可以跳过同步。例如,拥有HashMap并在从Map:
  4. 读取现有值时跳过同步是正确的

    实施例

    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();
        }
    }
    }
    

    您能否指出一些有关Java中多线程代码低级管理的宝贵文档?

    伙计们,我真的明白我们不应该依赖于特定的架构/ CPU /等等。我只是好奇所描述的点的概率大于0:)

    提前谢谢

1 个答案:

答案 0 :(得分:4)

除非您对访问进行同步或使变量易变,否则不应对线程看到由其他线程修改的值做出任何假设。

任何其他行为都不可靠,可能会发生变化。

请记住,Java在JVM上运行,而不是直接在您的处理器上运行,并且有许可对您运行的代码进行大量优化。因此,虽然很多行为都会延续,但你不能依赖它。特别是因为只要您在不同的架构上运行或在不同的条件下运行,完全相同的字节码可能会有不同的优化。