Сначала удалите весь код CompleteableFuture.
Для запусков вы хотите отобразить GUI, который является вашим JFrame, в потоке EventDispatch. Таким образом,
Runnable runnable = new Runnable() {
public void run() {
new GUI().setVisible(true);
}
}
EventQueue.invokeLater(runnable);
Это приведет к отображению вашего JFrame.
Ваш myList является локальной переменной в методе main (). Вместо этого создайте класс модели для хранения ваших переменных. Для простого приложения вам не нужно беспокоиться о синхронизации, если вы выполняете всю свою работу синхронно в потоке диспетчеризации событий, что является поведением по умолчанию.
Наконец, вам нужен класс контроллера, который связывает вашу модель и классы GUI вместе. Этот класс добавляет слушатели к классу GUI и обрабатывает вашу логику для обновления вашей модели и передачи значений из модели обратно в класс GUI.
Как только у вас есть базовые классы MVC и ваше приложение работает, вы можете использовать потоки для выгрузки работы из EDT. Это не обязательно, если выполняемая работа не занимает «много времени», и в этом случае ваш пользовательский интерфейс станет вялым.
Здесь ключ заключается в том, чтобы после выполнения работы предоставить еще один Runnable в EventQueue.invokeLater () для обновления пользовательского интерфейса. Это гарантирует, что вся работа, выполняемая в пользовательском интерфейсе, выполняется одним потоком.