In short
It is not written black on white, but if you fork you will have some constraints that will sooner or later require you to join in practice. There is only one valid construct that let you avoid the join in practice.
In more details
It is an indirect consequence of the concurrent flow of tokens and the rules for ending the flow.
According to UML 2.5 specifications, a flow-final has the following behavior:
A FlowFinalNode is a FinalNode that terminates a flow. All tokens
accepted by a FlowFinalNode are destroyed. This has no effect on other
flows in the Activity.
You can use it to absorb the tokens on one of the concurrent flows, without impact on the other branches.
Similarly, the activity-final obeys the following principle:
An ActivityFinalNode is a FinalNode that stops all flows in an
Activity. A token
reaching an ActivityFinalNode owned by an Activity terminates the
execution of that Activity.
But there is no rule that defines the speed of each flows, nor that each flow will perform all the expected activities if you do not have a join. This is the problem of the missing synchronisation.
Imagine that you have two forked flows that remain independent: each flow will then have to end somehow. If you have no join:
If an Activity owns more than one
ActivityFinalNode, then the first one to accept a token (if any)
terminates the execution of the Activity, including the execution of
any other ActivityFinalNodes.
So the first flow that reaches the activity-final will interrupt the ongoing activity on the other flows, leaving the performance of the other flows incomplete.
The only way to ensure that both branches would end correctly without interruption, would therefore to have a flow-final on each branch.
If one of the flow cycles you have the problem of the multiplication of tokens du to the concurrent flows, that are reinjected in the circuit (which correspond to concurrent execution).
The only other way to avoid a join node, is to use an implicit join:
An ExecutableNode shall not execute until all incoming ControlFlows
(if any) are offering tokens. That is, there is an implicit join
on the incoming Control Flows. Specific kinds of ExecutableNodes may
have additional prerequisites that must be satisfied before the node
can execute.
But implicit or explicit, it's a join ;-)