I haven't done this personally but here are some thoughts.
As you may already know, there's AsyncApexJob that holds instance's background jobs (finished or otherwise).
So I think, you can do a simple SOQL when your batch apex starts, i.e.:
public Database.QueryLocator start(Database.BatchableContext bc) {
Id thisClassId = '<id_of_you_batch_apex_class>'; //can be in a custom label for easier maintenance.
Id thisJobId = bc.getJobId();
AsyncApexJob[] jobs = [select id from AsyncApexJob where id!=:thisJobId AND status='Processing' AND ApexClassId=:thisClassId AND JobType='BatchApex'];
if (jobs==null || jobs.size()==0) {
return null;
}
else {
//return records to process here
}
}
These are all based on the documentation of AsyncApexJob object.
Basically, the SOQL is trying to get existing batch jobs (JobType='BatchApex'
) that is currently in processing status (status='Processing'
) using the current batch apex class (ApexClassId=:thisClassId
) and is NOT the currently running job (id!=:thisJobId
).
Again, I haven't tried this personally, but this could be it.
Good luck :)