You can add a test, in the beginning of the script, to check whether there are another early started instances of the script and while this is true the current instance will sleep. Something like that:
while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do sleep 1
done
Where:
the variable $0 contains the script name (and execution path);
the variable $$ contains the PID of the current running instance of the script;
ps aux - output the current processes;
grep -v 'grep' - preserve the lines that contains grep within the output of the previous command;
grep "$0" - output only the lines that are related to the current script name;
sed -e "/$$/p" -e "/$$/,$ d" - remove the newer instances of the script; remove all lines after the line that contains the PID of the current instance;
grep -vq "$$" - this is the actual test -q that will return 0 (true - there is at least one older instance of the script) or 1 (false - apparently this is the newest existing instance of the script) when the line with PID of the current instance is removed -v.
Here is a complete example:
#!/bin/bash
while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do
sleep 1
echo -n '.'
done
date +"%T"
for i in {1..5};do
echo "hello$i"
sleep 2
done
Here is the test I've made:

In addition you can create a launcher script that will execute your actual script in the above manner.
$ cat ~/Desktop/test-cron-launcher.sh
#!/bin/bash
LAUNCH_TIME="$(date +"%T")"
while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do sleep 1
done
echo "Job from $LAUNCH_TIME begin at $(date +"%T")"
. "$HOME/Desktop/test.sh"
$ cat ~/Desktop/test.sh
#!/bin/bash
for i in {1..40};do
echo "hello$i"
sleep 2
done
$ crontab -l | grep 'test'
30,31 12 * * * "$HOME/Desktop/test-cron-launcher.sh" >> "$HOME/Desktop/test.log" 2>&1
Read also: