2014-01-17 81 views
2

比方说,我有两个核心的CPU。如果我将运行与Executors.newFixedThreadPool后台处理服务(4)线程是我纠正:与CPU核心的Java线程关系

  1. 执行服务单线程的一生中会在不同的内核
  2. 即使如果线程A运行不同步运行它的代码放在核心1上,然后在CPU缓存中留下一些共享单例的值,然后如果线程B会在相同的核心上运行它的代码,并且会尝试从同一个内存位置获得单值,它会从CPU核心L1或L2缓存中获取它。如果线程B将使用同步,它将从主内存(最新版本)读取新值。通常,如果CPU内核中的某个线程缓存了共享对象的专用字段的某个值 - 另一个可以在同一个内核上运行的线程可以从其他线程留下的缓存中看到私有成员的值。
  3. 如果上面的两个选项都是真的 - 如果L2缓存将用于存储线程之间的共享(这将增加新的值映射)HashMap实例和L2将在所有核心缓存之间共享 - 这是否意味着跳过不是原子操作(如果我们只想在地图中看到正确/最新的值),我们可以跳过同步。例如会是正确的有一个HashMap和跳跃从地图读取现有的值同步:

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 :)

THX更大的概率

+0

当您执行Java时,请不要在二级高速缓存中考虑。这是一种抽象,可能会有不同的表现。如果你想重复使用每个线程的资源或不做任何超出[spec](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17。)的假设,请使用'ThreadLocal'。 HTML)。 – zapl

+0

“你能指点我一些关于java中低层管理多线程代码的有价值的文档吗?”点击上面评论中的“spec”。 – zapl

+0

我认为有时候我们应该依赖于特定的架构:) – MariuszS

回答

4

你不应该对线程看到其他线程修改的值作出任何假设,除非您在访问上同步或使变量变为易失性。

任何其他行为是不可靠的,可能会改变。

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

+0

是的,我明白我们不应该依赖特定的体系结构和JVM/CPU优化。我只是好奇,如果描述中描述的点可能在某些条件下发生。 – user253202

+0

1和2可能发生。在3之下,即使对于原子操作,也不能跳过同步,除非将变量变为易失性。正是出于这个原因,Java 5中的易失性被修改了。 –