代码之家  ›  专栏  ›  技术社区  ›  Carl

Java多线程初学者问题

  •  9
  • Carl  · 技术社区  · 15 年前

    我正在研究一个科学应用,它有容易分离的部分,可以并行进行。所以,我将这些部分作为独立的线程写入到每个运行中,尽管这并不是因为将事物划分为线程的标准原因(即,不阻塞某些退出命令等)。

    几个问题:

    在标准的多核桌面上,这真的为我买了什么东西吗?也就是说,如果我有一个当前的JVM,线程是否会在单独的核心上运行,或者我必须做其他的事情?

    我几乎没有被所有线程读取(尽管从未写入)的对象。潜在的问题?这些问题的解决方案?

    对于实际的集群,您是否可以建议框架将线程分发到各个节点,这样我就不必自己管理这些节点(如果存在这样的话)?澄清:通过这个,我的意思是自动将线程转换为单个节点的任务,或者使整个集群看起来像一个单独的JVM(也就是说,它可以将线程发送到它可以访问的任何处理器)或者其他任何东西。基本上,在一个集群上以一种有用的方式实现并行化,因为我已经将它构建到了算法中,而我自己的工作量最小。

    附加:大多数评估包括集合比较(例如,联合、交集、包含),以及一些从键映射来获得相关集合。我在Fortran、C和C++中有一些有限的经验(10年前的科学计算学期和其他两个Hs-AP类)——如果我用这些语言中的一个Java前端绑定到一个算法后端,我会发现什么样的速度/易程度的并行化增益,什么样的头痛可能是我的Excel级别?Rience发现用这些语言实现这些操作了吗?

    3 回复  |  直到 15 年前
        1
  •  8
  •   Jon Skeet    15 年前
    • 是的,使用独立线程将在普通的JVM中使用多个内核,而不必做任何工作。

    • 如果任何东西只被读取过,那么多个线程应该可以读取它。如果可以使问题对象不可变(到 保证 它们永远不会改变)那就更好了

    • 我不知道你在考虑什么样的集群,但是你可能想看看 Hadoop . 注意分布式计算分布 任务 而不是 螺纹 (通常,无论如何)。

        2
  •  5
  •   erickson    15 年前

    多核使用

    JavaRunTimes常规地调度线程在所有可用的处理器和内核上同时运行。我认为可以限制这一点,但这需要额外的工作;默认情况下,没有限制。

    不可变对象

    对于只读对象,将其成员字段声明为 final ,这将确保在创建对象且从未更改时分配它们。如果字段不是 最终的 ,即使它在构建之后从未改变过,多线程程序中也可能存在一些“可见性”问题。这可能导致一个线程所做的分配对另一个线程不可见。

    应声明由多个线程访问的任何可变字段 volatile 或者使用其他并发机制来确保线程之间的更改是一致的和可见的。

    分布式计算

    在Java中最广泛使用的分布式处理这种性质的框架称为 Hadoop. 它使用一种称为 map-reduce.

    本机代码集成

    与其他语言集成是不值得的。由于它对原生编译器的自适应字节码,Java在计算任务的范围内已经非常快。假设另一种语言在没有实际测试的情况下速度更快是错误的。此外,使用JNI与“本机”代码集成是非常繁琐、容易出错和复杂的;使用像JNA这样更简单的接口非常缓慢,并且会很快擦除任何性能改进。

        3
  •  1
  •   Vitali    15 年前

    正如一些人所说,答案是:

    1. 核心上的线程-是。Java在很长一段时间内对原生线程有支持。大多数操作系统都提供了内核线程,这些线程可以自动调度到您拥有的任何CPU(实现性能可能因操作系统而异)。

    2. 简单的答案是一般来说是安全的。更复杂的答案是,在任何线程都可以访问对象之前,必须确保实际创建并初始化了对象。这是两种方法之一:

      • 让类装入器使用单例(和延迟类装入)为您解决问题:

        public class MyImmutableObject
        {
            private static class MyImmutableObjectInstance {
                private static final MyImmutableObject instance = new MyImmutableObject();
            }
            public MyImmutableObject getInstance() {
                return MyImmutableObjectInstance.instance;
            }
        }
        
      • 显式使用获取/释放语义以确保一致的内存模型:

        MyImmutableObject foo = null;
        volatile bool objectReady = false;
        
        // initializer thread:
        ....
        /// create & initialize object for use by multiple threads
        foo = new MyImmutableObject();
        foo.initialize();
        
        // release barrier
        objectReady = true;
        
        // start worker threads
        public void run() {
           // acquire barrier
           if (!objectReady)
               throw new IllegalStateException("Memory model violation");
        
           // start using immutable object foo
        }
        

      我不记得如何才能利用Java的内存模型来执行后者。我相信,如果我没记错的话,对易失性变量的写入相当于释放屏障,而对易失性变量的读取相当于获取屏障。此外,与对象相比,使布尔值易失性的原因是由于内存模型约束,对易失性变量的访问成本更高-因此,布尔值允许您强制执行内存模型,然后可以在线程中更快地访问对象。

    3. 如前所述,有各种各样的RPC机制。还有RMI,这是一种在远程目标上运行代码的本地方法。还有像Hadoop这样的框架,它提供了一个更完整的解决方案,可能更合适。

    4. 对于调用本机代码来说,它非常难看——Sun确实不鼓励使用JNI,因为它使JNI成为一个难看的复杂混乱,但它是可能的。我知道至少有一个商业Java框架用于加载和执行原生动态库,而不必担心JNI(不确定是否有任何空闲或OSS项目)。

    祝你好运。