我在java中有一个简单的 Hadoop作业,它有一个mapper逐行处理我的文件。每个映射器都不受CPU约束,但应该在内存中保存一个非常大的对象(在我的情况下是一个Bloom Filter),其大小为2-15千兆字节(取决于计算精度)。在Mapper的setup()
方法中,我从磁盘读取此对象并创建它。
我遇到了MultithreadedMapper
类,在多个线程中执行计算。
job.setMapperClass(MultithreadMapper.class);
// ...
MultithreadedMapper.setMapperClass(job, MySingleThreadMapper.class);
MultithreadedMapper.setNumberOfThreads(job, 16);
但似乎MultithreadedMapper
使用内部private class MapRunner extends Thread
来生成线程映射器:
public class MultithreadedMapper<K1, V1, K2, V2> extends Mapper<K1, V1, K2, V2> {
//...
public void run(Context context) throws IOException, InterruptedException {
// ...
runners = new ArrayList<MapRunner>(numberOfThreads);
for(int i=0; i < numberOfThreads; ++i) {
MapRunner thread = new MapRunner(context);
thread.start();
runners.add(i, thread);
}
}
}
这是一个问题:如何在MultithreadedMapper中创建一个非常大的对象,并在集群节点(相同的jvm)的线程映射器之间共享它(使用上下文或其他)?
我尝试通过Singleton模式实现它,但是如果看起来不是一个漂亮的解决方案。
答案 0 :(得分:1)
序言:我之前从未这样做过,但我会使用静态锁实现初始化:
static class MySingleThreadMapper extends Mapper<LongWritable, Text, Text, Text> {
static MyResource sharedResource;
@Override
protected void setup(Context context) throws IOException, InterruptedException {
super.setup(context);
synchronized (MySingleThreadMapper.class) {
if (sharedResource == null) {
sharedResource = createResource();
}
}
}
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// mapper code
// sharedResource will be initialized here
}
}
您可能已经知道,Hadoop会在单独的JVM实例中生成Map和Reduce任务。因此,所有单线程映射器都将在同一个JVM中运行,您可以依赖静态锁定。您可以将任何其他静态对象用作锁,您的共享资源只会被初始化一次。