记录一下:
Thread States
A thread can be only in one of five states (see Figure 9-2):
New :
This is the state the thread is in after the Thread instance has been created, but the start() method has not been
invoked on the thread. It isa live Thread object, but not yet a thread of execution. At this point, the thread is considered
not alive.
Runnable:
This is the state a thread is in when it's eligible to run, but the scheduler has not selected it to be the running thread.
A thread first enters the runnable state when the start() method is invoked, but a thread can also return to the runnable
state after either running or coming back from a blocked, waiting, or sleeping state. When the thread is in the runnable
state,it is considered alive.
Running :
This is it. The "big time." Where the action is. This is the state a thread is in when the thread scheduler selects it (from the
runnable pool) to be the currently executing process. A thread can transition out of a running state for several reasons,
including because "the thread scheduler felt like it."We'll look at those other reasons shortly. Note that in Figure 9-2, there
are several ways to get to the runnable state, but only one way to get to the running state: the scheduler chooses a
thread from the runnable pool.
Waiting/blocked/sleeping:
This is the state a thread is in when it's not eligible to run. Okay, so this is really three states combined into one,
but they all have one thing in common: the thread is still alive, but is currently not eligible to run. In other words, it is
not runnable, but it might return to a runnable state later if a particular event occurs. A thread may be blocked
waiting for a resource (like I/O or an object's lock), in which case the event that sends it back to runnable is the
availability of the resource—for example, if data comes in through the input stream the thread code is reading from,
or if the object's lock suddenly becomes available. A thread may be sleeping because the thread's run code tells it to
sleep for some period of time,in which case the event that sends it back to runnable is that it wakes up because its sleep
time has expired. Or the thread may be waiting, because the thread's run code causes it to wait, in which case the
event that sends it back to runnable is that another thread sends a notification that it may no longer be necessary for the
thread to wait. The important point is that one thread does not tell another thread to block. Some methods may look
like they tell another thread to block, but they don't. If you have a reference t to another thread, you can write something
like this:
t.sleep(); or t.yield()
But those are actually static methods of the Thread class—they don't affect the instance t; instead they are defined
to always affect the thread that's currently executing. (This is a good example of why it's a bad idea to use an instance
variable to access a static method—it's misleading. There is a method,suspend(), in the Thread class, that lets one thread
tell another to suspend,but the suspend() method has been deprecated(nor resume()). There is also a stop() method, but
it too has been deprecated and we won't even go there. Both suspend()and stop() turned out to be very dangerous, so
you shouldn't use them. Don't study 'em, don't use 'em. Note also that a thread in a blocked state is still considered to be
alive.
Dead :
A thread is considered dead when its run() method completes. It may still be a viable Thread object, but it is no longer a
separate thread of execution. Once a thread is dead, it can never be brought back to life! (The whole "I see dead threads"
thing.) If you invoke start() on a dead Thread instance, you'll get a runtime (not compiler) exception. And it probably
doesn't take a rocket scientist to tell you that if a thread is dead, it is no longer considered to be alive.
sleep():
The sleep() method is a static method of class Thread. You use it in your code to "slow a thread down" by forcing it
to go into a sleep mode before coming back to runnable (where it still has to beg to be the currently running thread).
When a thread sleeps, it drifts off somewhere and doesn't return to runnable until it wakes up.
yield():
In most JVMs, however, the scheduler does use thread priorities in one important way: If a thread enters the runnable
state, and it has a higher priority than any of the threads in the pool and a higher priority than the currently running thread,
the lower-priority running thread usually will be bumped back to runnable and the highest-priority thread will be chosen to
run. In other words, at any given time the currently running thread usually will not have a priority that is lower than any of
the threads in the pool. In most cases, the running thread will be of equal or greater priority than the highest priority threads
in the pool. This is as close to a guarantee about scheduling as you'll get from the JVM specification, so you must never rely
on thread priorities to guarantee the correct behavior of your program.
So what does the static Thread.yield() have to do with all this? Not that much, in practice. What yield() is supposed
to do is make the currently running thread head back to runnable to allow other threads of the same priority to get their turn.
So the intention is to use yield() to promote graceful turn-taking among equal-priority threads. In reality, though, the yield()
method isn't guaranteed to do what it claims, and even if yield() does cause a thread to step out of running and back to
runnable, there's no guarantee the yielding thread won't just be chosen again over all the others! So while yield() might—
and often does—make a running thread give up its slot to another runnable thread of the same priority, there's no guarantee.
A yield() won't ever cause a thread to go to the waiting/sleeping/ blocking state. At most, a yield() will cause a thread to
go from running to runnable, but again, it might have no effect at all.
join():
The non-static join() method of class Thread lets one thread "join onto the end"of another thread. If you have a thread B
that can't do its work until another threadA has completed its work, then you want thread B to "join" thread A. This means that
thread B will not become runnable until A has finished (and entered the dead state).
Thread t = new Thread();
t.start();
t.join();
The preceding code takes the currently running thread (if this were in the main() method, then that would be the main
thread) and joins it to the end of the thread referenced by t. This blocks the current thread from becoming runnable until after
the thread referenced by t is no longer alive. In other words, the code t.join() means "Join me (the current thread) to the
end of t, so that t must finish before I (the current thread) can run again." You can also call one of the overloaded versions of
join() that takes a timeout duration, so that you're saying, "wait until thread t is done, but if it takes longer than 5,000millise-
conds, then stop waiting and become runnable anyway."
wait(),notify(),notifyAll():
wait(), notify(), and notifyAll() must be called from within a synchronized context! A thread can't invoke a wait
or notify method on an object unless it owns that object's lock.
The methods wait() and notify(), remember, are instance methods of Object.In the same way that every object has
a lock, every object can have a list of threads that are waiting for a signal (a notification) from the object. A thread gets on
this waiting list by executing the wait() method of the target object. From that moment, it doesn't execute any further
instructions until the notify() method of the target object is called. If many threads are waiting on the same object, only
one will be chosen (in no guaranteed order) to proceed with its execution. If there are no threads waiting, then no particular
action is taken. For a thread to call wait() or notify(), the thread has to be the owner of the lock for that object. When the
thread waits, it temporarily releases the lock for other threads to use, but it will need it again to continue execution. When the
wait() method is invoked on an object, the thread executing that code gives up its lock on the object immediately. However,
when notify() is called,that doesn’t mean the thread gives up its lock at that moment. If the thread is still completing
synchronized code, the lock is not released until the thread moves out of synchronized code. So just because notify() is called
doesn’t mean the lock becomes available at that moment.
In most scenarios, it's preferable to notify all of the threads that are waiting on a particular object. If so, you can use
notifyAll() on the object to let all the threads rush out of the waiting area and back to runnable. This is especially important
if you have several threads waiting on one object, but for different reasons, and you want to be sure that the right thread
(along with all of the others) gets notified.
As we said earlier, an object can have many threads waiting on it, and using notify() will affect only one of them. Which
one, exactly, is not specified and depends on the JVM implementation, so you should never rely on a particular thread being
notified in preference to another.