Для начала, мне непонятно, почему все части вашей программы, которые вполне могут выполняться синхронно, работают в потоках. Есть часть программы, которая выиграла бы от многопоточности − сканирование портов, где логично было бы создать по потоку на каждый порт (если портов слишком много, то создать пул потоков, определить стратегию переиспользования потоков, в общем, тут большой простор для творчества), но у вас порты сканируются в одном потоке, последовательно.
Но давайте притворимся, что так и надо, и поговорим о других проблемах вашего кода.
- Не стоит использовать глобальные переменные в программах длиннее одного экрана. Вместо globalлучше объявить глобальный объект и спрятатьflagв него:
 class PortScanner:
    
    def __init__(self):
        self.flag = 0
        
    def animate_menu_up(self):
        print("\n")
        ...
        self.flag = 1
 
 
 
- Код инициализации программы тоже лучше перенести в __init__()глобального объекта. И основной цикл вынести в отдельный метод, например,run(). Тогда на нижнем уровне у нас останется что-то вроде:
 import ...
class PortScanner:
    ...
if __name__ == '__main__':
    main_obj = PortScanner()
    main_obj.run()
 
 этот идиоматичный код позволит импортировать класс PortScanner в другой скрипт, а также обеспечит плюсик на собеседовании/ревью,
 
- приучайтесь использовать докстринги вместо комментариев:
 def animate_menu_up():
    """ Создание красивого меню (вверх). """
 
 
- лишний цикл while True:вselect_mode(),
 
- слишком много магии. По мере роста программы становится всё тяжелее держать и сопоставлять в голове всякие абстрактные значения. Вот литералы, которые, по моему мнению, стоило бы определить как константы − или в «шапке» скрипта, или как атрибуты класса:
 MF_INITIAL = 0
MF_MENU_CENTER = 1
MF_MENU_DOWN = 2
MF_SELECT = 3
SCREEN_WIDTH = 50
ALL_PORTS = [22, 80, 7777, 2516]
SOCK_TIMEOUT = 0.5
ANIM_SYMBOL = '~'
ANIM_DELAY = 0.02
 
 
- такие вещи очень больно стреляют в ногу и почти гарантированно проваливают собеседования:
 except RuntimeError:
    continue
 если вы действительно хотите продолжить выполнение программы после такой ошибки (что в обычных обстоятельствах бессмысленно и опасно), то позаботьтесь хотя бы о правильной индикации:
 import traceback
...
except RuntimeError:
    traceback.print_exc(file=sys.stdout)
    continue
 
 
- раз уж мы задержались здесь, давайте сделаем диспетчер более идиоматичным:
 while True:
    try:
        {
            MF_MENU_CENTER: th_three.start,
            MF_MENU_DOWN: th_two.start,
            MF_SELECT: select_mode,
        }[flag]()
    except RuntimeError:
        traceback.print_exc(file=sys.stdout)
        continue
 Такой наивный подход выводит много шелухи на экран, но это не важно. Важно то, что такой код проще читать и дорабатывать, чем цепочкуif...elif...else.
 
 Работа со строками тоже напрашивается на улучшения:- нет смысла разбивать строки на списки, к символам в строке можно обращаться так же, как к элементам списка − по индексу и с помощью срезов,
- не нужно копировать строки,
- не нужно печатать всю строку с начала, это делает анимацию неровной − конец строки печатается медленнее, чем начало. Достаточно допечатать один символ в строке.
 Например:- def animate_menu_center():
    """ Создание анимации центрального меню. """
    # настройки для анимации
    output_strings = [
        '[1] scan all ports',
        '[2] scan enter port',
        '[3] exit',
        'SCANNER V1.0',
    ]
    # анимация названия
    for output_string in output_strings:
        print('\r\t\t', end='')
        for ch in output_string:
            print(ch, end='')
            time.sleep(ANIM_DELAY)
        # last string?
        if output_string != output_strings[-1]:
            # new line
            print()
    print('', flush=True)
    flag = MF_MENU_DOWN
 (Я убрал цвета для простоты.)
 
- юзабилити сильно страдает из-за отсутствия нормальной функции выхода из приложения,
 
- не оставляйте закомментированный код в файле, который отдаёте на ревью, это минус. Вместо этого напишите комментарий с пометкой “TODO”, например:
 # TODO: реализовать режим '2' (скан произвольного списка портов)
 Это однозначно плюс − показывает, что вы умеете работать в команде и пользоваться системами контроля версий.
 
Вроде бы наиболее серьёзные проблемы я перечислил. Выложу более полный вариант кода в комментариях.