Вот сложный, но переносимый способ.
Разделите ваш код на две банки. Одна очень маленькая банка предназначена только для управления запуском процесса. Он создает ClassLoader, который содержит другой jar на своем пути к классам.
Когда вы хотите загрузить новую версию, вы прекращаете все потоки, выполняющие код из старого jar. Обнулить все ссылки на экземпляры классов из старого фляги. Удалите все ссылки на ClassLoader, который загрузил старый jar. На этом этапе, если вы ничего не пропустили, старые классы и ClassLoader должны иметь право на сборку мусора.
Теперь вы начинаете все заново с нового экземпляра ClassLoader, указывающего на новый jar, и перезапускаете код приложения.