The behavior you refer to is called "continuation threads." Since the join() method doesn't do a context switch, the framework will stall without those additional threads. There have been reports of hundred/thousands of these extra threads spawned.
Java8 replaces this awful practice with "continuation threads." That is, the thread continues fetching tasks from the deque, but it can result in a stall or a stack overflow. There is no replacement for code that uses ForkJoinPool.managedBlock() which may be where you are at with the I/O.