Куча - это просто вся память вне стека, которая выделяется динамически.Поскольку ОС предоставляет единое адресное пространство, становится ясно, что куча по определению является общей для всех потоков процесса.Что касается того, почему стеки не используются совместно, то это потому, что поток выполнения должен иметь свой собственный стек, чтобы иметь возможность управлять своим деревом вызовов (например, он содержит информацию о том, что делать, когда вы покидаете функцию!).
Теперь вы, конечно, могли бы написать менеджер памяти, который распределял данные из разных областей в вашем адресном пространстве в зависимости от вызывающего потока, но другие потоки все равно могли бы видеть эти данные (как если бы вы каким-то образом просочились на указательстек вашего потока в другой поток, чтобы другой поток мог его прочитать, несмотря на то, что это ужасная идея)