Multi-threading allows a single program to execute multiple programs simultaneously by utilizing CPU processing time more efficiently and reducing idle time. Threads can be created by implementing the Runnable interface or extending the Thread class and go through life cycle stages of new, runnable, blocked, and dead. The join method allows a parent thread to wait for child threads to terminate before ending, and synchronization techniques ensure shared resources are accessed properly between threads.