I'm fond of using UML diagrams to describe my software. In the majority of cases the diagrams are for my own use and I use them for more involved pieces of code, interactions etc. where I'll benefit from being able to look back over them in the future.
One thing I've found myself doing a few different ways is diagramming threads. Threads by their nature tend to pop up in the more involved pieces of code and keeping track of them is often a primary purpose of my design documents.
In the past I've used a symbol in a sequence diagram to show the creation of a new thread but looking back at some diagrams doing that it's sometimes ambiguous between an object's lifetime - which sequence diagrams are for - and a thread's lifetime. Is there a better approach for incorporating threads into UML?
I managed to produce a diagram that makes sense to me at the time of drawing it. The basic premise is that I've overlaid grey boxes representing class instances with blue boxes representing thread lifetimes. The main thing it lets me keep track of is knowing which thread I will be executing on when I call certain methods.
No doubt there's better and more intuitive ways to do thread and class modeling. The measure of success for me is whether my own diagram still gives me the same level of understanding 6 months down the track.
Activity, Sequence, and State Diagrams are all correct ways of showing thread behavior.
1st: (To vs's comments) There are two sets of diagrams or modeling elements in UML, static structure, as you put it, and behavioral. Any book will help you understand the split, typically in the contents/TOC, additionally it can be seen on page 11 of Martin Fowler's UML Distilled a near defacto standard for beginning UML in my opinion.
2nd: (To sipwiz's question and comment) Activity diagrams are not commonly understood to model business process, they can be used for that however, and most examples or simple tutorial would approach it from a business standpoint.
Discussion on your options to model threads:
Activity diagrams - Allows for forking and specifying concurrency by using a BAR and usage lines. Note the example at the bottom is no a business process, example. Most people can read these, business, management, and developers, though sometimes they can lack detail or get messy.
Sequence Interaction diagrams - In the same post, example, you will see sequence diagrams allow you to specify parallel behavior within a sequence by boxing parallelizable behavior with a label "par", this is useful to show the reader what methods can or should be called in parallel, ie, by different threads. This is the method I would use for detailed developer like discussions around building an object.
State diagram - The state chart just like the activity allows for concurrency by using a BAR and usage lines.
NOTE: These will not model a specific thread and it's exact lift cycle, as that is part of the instance/run-time level of modeling, if this what you want clarify your question and I will respond. I would just model it using one of the above as no one other than a MDA/UML expert will call you out, and you are not generating a running system.
Also: Please note that further details can be found in most UML books. Also leveraged: http://www.jguru.com/faq/view.jsp?EID=56322
Traditionally threading has been depicted diagramatically using Petri Nets. Rob Martin has an article on multithreading in UML which you may find useful.
Update- just remembered you can represent threads with forks in activity diagrams- I've managed to find something that explains this.
It is very hard to find any free tutorials for Petri Nets, however I know Petri Nets are good for modeling concurrency, so I Google'd "producer-consumer Petri Nets" (my favourite threading thing) and found this.
I've also found some slides that show Petri Nets modeling a Semaphore.
UML activity diagrams have fork and join elements to show parallel flow of logic.
I don't know of a way, but using a sequence diagram does not seem entirely inappropriate, considering that a thread is in many languages implemented as a
Thread (or similar) class.
The most UML-compatible way would probably be to add an annotation of some sort indicating that the 'object' represents a thread.
The UML is defined by the UML Superstructure, you can find it here http://www.omg.org/spec/UML. If you read the specification you find that a UML class can be active. An Active Class is a class with the meta-attribute isActive set to true. It is also depicted differently. An object instances of an active class automatically executes a "classifier behavior". As for any behavior you can define it by means of an activity in which you wait for asynchronous signals (AcceptEventActions) and invokes methods (CallOperationAction) or other behaviors (CallBehaviorActions). That is how active objects are modeled in UML. You just have to read the UML specification.
Activity diagrams will model the internal workings of your software with forks and joins to represent threads. To find out exactly how to model this properly, please see Conrad Bock's excellent series of articles. Here is the article that covers forks and joins, but you should follow the links back to the first article in the series to learn how to properly model using "Colored Petri Nets". It's not how you think (and it's pretty easy)!
There is a new, in-process standard at the OMG for a language called Alf that provides a more convenient surface notation for activity diagrams and is intended for representing code. From the spec:
A primary goal of an action language is to act as the surface notation for specifying executable behaviors within a wider model that is primarily represented using the usual graphical notations of UML. For example, this might include methods on the operations of classes or transition effect behaviors on state machines.
For a programmer, you probably can't get more intuitive than Alf. And it will convert perfectly into UML activity diagrams.
UML strongest point is depicting the static structure. If you use short-lived threads, I also don't see any easy way of diagramming them. Maybe you can find a solution by turning things around a bit: why do you use/need threads? What's the functionality they provide? If they interact with each other and follow some (message passing) API, drawing them as components might make sense.