The reason for the failure can be found in man 2 flock:
Locks created by flock() are associated with an open file description (see open(2)). This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock, and this lock may be modified or released using any of these descriptors.
This means that because all your processes inherit the same file descriptor, when one of them does a lock, they all share it. And locking the same file descriptor twice is a no-op.
My usual solution to stuff like this is to lock the script itself (though this does pose problems if you run the script multiple times simultaneously).
#!/bin/bash
fun()(
exec 3<"$0"
flock 3 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
echo "$BASHPID begin"
sleep 1;
echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait