你在这里有一个掩盖原始错误的后续错误.
当lambda实例被序列化时,他们使用writeReplace来解析它们的JRE特定
实施从持续的形式是一个SerializedLambda
实例.当SerializedLambda实例被还原时,它的readResolve方法将被调用
重建适当的lambda实例.正如文档所说,它将通过调用定义原始lambda的类的特殊方法(参见this answer)来实现.重要的一点是,原来的课程是需要的,这就是你的情况中缺少的东西.
但是有一个…特殊的… ObjectInputStream的行为.当它遇到异常时,它不会立即缓解.它将记录异常并继续该过程,标记当前正在读取的所有对象,因此取决于错误的对象也是错误的.只有在进程结束时才会抛出它遇到的原始异常.令它如此奇怪的是,它也将继续尝试设置这些对象的字段.但是当您查看ObjectInputStream.readOrdinaryObject行1806方法时:
…
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
您会发现在lookupException报告非空异常时,它不会调用readResolve方法.但是当替代没有发生时,继续尝试设置引用来源的字段值并不是一个好主意,但这正是在这里发生的,从而产生一个ClassCastException.
您可以轻松地重现问题:
public class Holder implements Serializable {
Runnable r;
}
public class Defining {
public static Holder get() {
final Holder holder = new Holder();
holder.r=(Runnable&Serializable)()->{};
return holder;
}
}
public class Writing {
static final File f=new File(System.getProperty("java.io.tmpdir"), "x.ser");
public static void main(String... arg) throws IOException {
try(FileOutputStream os=new FileOutputStream(f);
ObjectOutputStream oos=new ObjectOutputStream(os)) {
oos.writeObject(Defining.get());
}
System.out.println("written to "+f);
}
}
public class Reading {
static final File f=new File(System.getProperty("java.io.tmpdir"), "x.ser");
public static void main(String... arg) throws IOException, ClassNotFoundException {
try(FileInputStream is=new FileInputStream(f);
ObjectInputStream ois=new ObjectInputStream(is)) {
Holder h=(Holder)ois.readObject();
System.out.println(h.r);
h.r.run();
}
System.out.println("read from "+f);
}
}
编译这四个类,并运行写作.然后删除类文件Defining.class并运行阅读.然后你会得到一个
Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.lang.invoke.SerializedLambda to field test.Holder.r of type java.lang.Runnable in instance of test.Holder
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2089)
at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1261)
(测试1.8.0_20)
最基本的一点是,一旦知道发生了什么,您可能会忘记这个序列化问题,解决问题所需要做的只是确保定义lambda表达式的类在lambda的运行时也可用反序列化.
Spark作业直接从IDE运行的示例(spark-submit默认分发jar):
SparkConf sconf = new SparkConf()
.set("spark.eventLog.dir", "hdfs://nn:8020/user/spark/applicationHistory")
.set("spark.eventLog.enabled", "true")
.setJars(new String[]{"/path/to/jar/with/your/class.jar"})
.setMaster("spark://spark.standalone.uri:7077");