Here are a few concurrency tricks if you're stuck using threads. I used these tricks years ago to write a Swing application in Jython, and I found them to be helpful enough to warrant a blog post, albeit a few years delayed.
First, let's suppose you have a UI and you want to talk to an external program written in another language that might occasionally block. Use the main thread for the UI and use a separate thread to coordinate with the external program. In general, most things that might block should have their own thread.
Name your threads.
It's likely that certain code should only be run by the UI thread and vice versa. At the top of each method, do an assertion on the thread name.
Avoid sharing data. Sharing data involves mutexes, etc. which is generally painful and easy to mess up. Instead, constrain each bit of data to a single thread. If you need to interact with that data from another thread, "ask the other thread for help".
The way I like to do this is to use the queue module which takes care of its own locking. You can pass requests from one thread to another via a queue. In some cases, I even found it helpful to pass a callback on the queue. That's like saying, "Call this callback, but do it from your own thread."
Much of this approach now falls under the label of "actors". An actor is the combination of an input queue, a thread, and an object. I like to think of them as "living objects that you talk to asynchronously." However, even years before I had heard the term actor or knew how to code in Erlang, these tricks were still useful.