问题
I've had an optaplanner project working for a while now that is allocating jobs to a list of workers using various scoring properties all written in java. Happy with the way it was working I decided to update from 7.7.0 to 7.9.0 and turn on the new multithreading option and see if it works any faster. However, I now get the following error:
Exception in thread "Thread-8" java.lang.IllegalStateException: The move thread with moveThreadIndex (0) has thrown an exception. Relayed here in the parent thread. Caused by: java.lang.IllegalArgumentException: The externalObject (...Worker@72d2327a) cannot be looked up. Maybe give the class (class Worker) a PlanningId annotation or change the PlanningSolution annotation's LookUpStrategyType or don't rely on functionality that depends on ScoreDirector.lookUpWorkingObject().
So I tried adding @PlanningId to the unique identifier of the worker class and I get:
Exception in thread "Thread-7" java.lang.IllegalStateException: The workingObjects (Worker@4438b624, Worker@4438b624) have the same planningId ((class Worker,50)). Working objects must be unique.
...but it looks like it's comparing a Worker object with itself, so of course it's the same id????
Perhaps this is down to my java code in the assign/unassign methods but I don't understand where this issue is coming from, anyone have any ideas?
Edit: So after a lot of help from @n1ck I got as far as changing the method in the @PlanningSolution class that returns the list of Workers to create new instances each time but with the same values. This the changes the error to be : '...The workingObjects (Worker@1eba2b4d, Worker@67a7ef08) have the same planningId...' So it's no longer comparing identical instances but does seem to be comparing it to an instance from a previous call. This does however at least confirm there is no duplication per List returned. So I then changed the planning id to be a new (Long) field whose value is always incremented from the last instance created i.e. first call Worker ids are 1-10, next call 11-20 and so on. This bypasses the error altogether but now nothing gets allocated to the Workers. It does the initial set up and null assigns and then stops until the time limit on the solver is reached. I still think this is something I haven't set right but the error is possibly a bit of a red herring, however as requested the full stack trace as I can see it is:
Exception in thread "Thread-10" java.lang.IllegalStateException: The workingObjects (Worker@42094cc1, Worker@647a1c5c) have the same planningId ((class Worker,50)). Working objects must be unique. at org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.addWorkingObject(PlanningIdLookUpStrategy.java:40) at org.optaplanner.core.impl.domain.lookup.LookUpManager.addWorkingObject(LookUpManager.java:49) at org.optaplanner.core.impl.domain.lookup.LookUpManager.resetWorkingObjects(LookUpManager.java:43) at org.optaplanner.core.impl.score.director.AbstractScoreDirector.setWorkingSolution(AbstractScoreDirector.java:167) at org.optaplanner.core.impl.score.director.incremental.IncrementalScoreDirector.setWorkingSolution(IncrementalScoreDirector.java:62) at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:230) at org.optaplanner.core.impl.solver.AbstractSolver.solvingStarted(AbstractSolver.java:75) at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:210) at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:190) at SolverThread.run(SolverThread.java:86)
Edit: @PlanningSolution class as requested:
package code.test.scheduler;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
import org.optaplanner.persistence.xstream.api.score.buildin.hardmediumsoft.HardMediumSoftScoreXStreamConverter;
import com.thoughtworks.xstream.annotations.XStreamConverter;
@PlanningSolution
public class Planner
{
@ProblemFactCollectionProperty
private java.util.List<Worker> _workerList;
@ValueRangeProvider(id="workerRange")
@ProblemFactCollectionProperty
public java.util.List<Worker> getWorkerList() {
return _workerList;
}
public void setWorkerList(java.util.List<Worker> newWorkerList) {
_workerList = newWorkerList;
}
public int getWorkerListSize() {
return _workerList.size();
}
@ProblemFactCollectionProperty
private int[][] _travelTimes;
@ValueRangeProvider(id="travelTimes")
@ProblemFactCollectionProperty
public int[][] getTravelTimes() {
return _travelTimes;
}
public void setTravelTimes(int[][] newTravelTimes) {
_travelTimes = newTravelTimes;
}
@PlanningEntityCollectionProperty
private java.util.List<Job> _jobList;
public java.util.List<Job> getJobList() {
return _jobList;
}
public void setJobList(java.util.List<Job> newJobList) {
_jobList = newJobList;
}
@XStreamConverter(HardMediumSoftScoreXStreamConverter.class)
private HardMediumSoftScore _score;
@PlanningScore
public HardMediumSoftScore getScore() {
return _score;
}
public void setScore(HardMediumSoftScore score) {
_score = score;
}
}
回答1:
I think you have some duplicate entities in your PlanningEntityCollection. Are you sure all Worker objects in this list are unique (as in, not the same instance)?
I duplicated an entity in my entity collection on purpose, and got the same error.
Edit: it turned out the issue was declaring the @ProblemFactCollectionProperty
annotation above both the field and getter. It should only be declared above one or the other. Declaring it above both the field and getter, in conjunction with using the @PlanningId
annotation, will result in the 'Working objects must be unique' error.
来源:https://stackoverflow.com/questions/51597744/optaplanner-7-9-0-and-adding-multithreading-same-planningid-exception