Exploring Threads 探索线程

Applications execute via threads, which are independent paths of execution through an application’s
code. When multiple threads are executing, each thread’s path can differ from other thread paths.
For example, a thread might execute one of a switch statement’s cases, and another thread might

execute another of this statement’s cases.

通过线程执行应用程序,线程是穿过应用程序代码的独立执行路径。当执行多个线程时,各个线程的路径能不同于其它线程路径。例如,一个线程可以执行一个switch语句的cases之一,另一个也许执行该语句的另一case。

Note Applications use threads to improve performance. Some applications can get by with only the default
main thread (the thread that executes the main() method) to carry out their tasks
, but other applications
need additional threads to perform time-intensive tasks in the background, so that they remain responsive to
their users.

注意 某些应用程序只能靠main线程(执行main()方法的线程)来完成它们的任务,

The virtual machine gives each thread its own method-call stack to prevent threads from interfering
with each other. Separate stacks let threads keep track of their next instructions to execute, which
can differ from thread to thread. The stack also provides a thread with its own copy of method
parameters, local variables, and return value.

虚拟机给予每个线程自己的方法-调用栈以避免线程彼此干扰。分开的栈让线程追踪它们的下一条要执行的指令,不同线程可能有不同的指令。栈也提供给线程自己的方法参数、局部变量和返回值的副本。

Java supports threads via its Threads API. This API largely consists of one interface (Runnable) and
four classes (Thread, ThreadGroup, ThreadLocal, and InheritableThreadLocal) in the java.lang
package. After exploring Runnable and Thread (and mentioning ThreadGroup during this exploration),
I explore synchronization, ThreadLocal, and InheritableThreadLocal.

Java 通过Threads API 支持线程。

Note Java 5 introduced the java.util.concurrent package as a high-level alternative to the low-
level Threads API. (I will discuss this package in Chapter 10.) Although java.util.concurrent is the
preferred API for working with threads, you should also be somewhat familiar with Threads because it’s
helpful in simple threading scenarios. Also, you might have to analyze someone else’s source code that
depends on Threads.

注意


Runnable and Thread

Java’s Runnable interface identifies those objects that supply code for threads to execute via this
interface’s solitary void run() method—a thread receives no arguments and returns no value. In the
following code fragment, an anonymous class implements Runnable:

Java的Runnable接口标识那些对象,这些对象为提供线程执行的代码,这些代码又是通过这个接口的单独的run()方法执行—线程不接收参数且不返回值。下面的代码段中,一个匿名类实现Runnable:

Runnable r = new Runnable()
{
@Override
public void run()
{
// perform some work
}
};

Java’s Thread class provides a consistent interface to the underlying operating system’s threading
architecture. (The operating system is typically responsible for creating and managing threads.) A
single operating system thread is associated with a Thread object.

Java的 Thread 类提供对底层操作系统的线程体系结构的一致接口。(操作系统典型地负责建立和管理线程。)单个操作系统线程关联一个Thread 对象。

Thread declares several constructors for initializing Thread objects. Some of these constructors take
Runnable arguments. For example, Thread(Runnable runnable) initializes a new Thread object to the
specified runnable whose code is to be executed, which the following code fragment demonstrates:

Thread t = new Thread(r);

Other constructors don’t take Runnable arguments. For example, Thread() doesn’t initialize Thread
to a Runnable argument. You must extend Thread and override its run() method to supply the code
to run, which the following code fragment accomplishes:

class MyThread extends Thread
{
@Override
public void run()
{
}
}

In the absence of an explicit name argument, each constructor assigns a unique default name
(starting with Thread-) to the Thread object. Names make it possible to differentiate threads. In
contrast to the previous two constructors, which choose default names, Thread(String threadName)
lets you specify your own thread name.

在没有显式名称参数的情况下, 每个构造器赋予一个唯一的默认名称(以Thread-打头)给这个线程对象。名称使得区别不同线程成为可能。和前面的两构造器(它们选择使用默认名称)相反,Thread(String threadName)让你指定你自己的线程名。

Thread also declares methods for starting and managing threads. Table 7-8 describes many of the
more useful methods.

able 7-8. Thread Methods
Method Description
static Thread currentThread() Returns the Thread object associated with the thread that calls this method.


String getName() Returns the name associated with this Thread object.


Thread.State getState() Returns the state of the thread associated with this Thread object. The state is
identified by the Thread.State enum as one of BLOCKED (waiting to acquire a lock,
discussed later), NEW (created but not started), RUNNABLE (executing), TERMINATED
(the thread has died), TIMED_WAITING (waiting for a specified amount of time to
elapse), or WAITING (waiting indefinitely).

Thread.State getState()返回和这个Thread 对象关联的线程的状态。状态由Thread.State值标识,枚举值可以是BLOCKED (等着获得一把锁,后面讨论),NEW(建立但未启动),RUNNABLE(执行中),TERMINATED(线程已死),TIMED_WAITING (等着指定的时间流逝),或WAITING (无限期等待)。


void interrupt()  Sets the interrupt status flag in this Thread object. If the associated thread is
blocked or is waiting, clear this flag and wake up the thread by throwing an
instance of the java.lang.InterruptedException class.

设置Thread对象的interrupt 状态标志。如果关联线程被阻止或正等待,通过扔出类java.lang.InterruptedException的一实例来清除这个标志并唤醒该线程。


static boolean  interrupted() Returns true when the thread associated with this Thread object has a pending
interrupt request. Clears the interrupt status flag.

当与这个Thread 对象关联的线程有一个挂起的中断请求就返回true。清除中断标志。


boolean isAlive() Returns true to indicate that this Thread object’s associated thread is alive and not
dead. A thread’s life span ranges from just before it is actually started within the
start() method to just after it leaves the run() method, at which point it dies.

boolean isDaemon() Returns true when the thread associated with this Thread object is a daemon
thread, a thread that acts as a helper to a user thread (nondaemon thread) and
dies automatically when the application’s last nondaemon thread dies so the
application can exit.


boolean isInterrupted() Returns true when the thread associated with this Thread object has a pending
interrupt request.


void join() The thread that calls this method on this Thread object waits for the thread
associated with this object to die. This method throws InterruptedException
when this Thread object’s interrupt() method is called.

 调用在这个线程对象上的这个方法的线程等待与这个对象相关联的线程死去。当这个线程对象的interrupt方法被调用时,这个方法抛出 InterruptedException。


void join(long millis) The thread that calls this method on this Thread object waits for the thread
associated with this object to die, or until millis milliseconds have elapsed,
whichever happens first. This method throws InterruptedException when this
Thread object’s interrupt() method is called.


void setDaemon(boolean isDaemon)
Marks this Thread object’s associated thread as a daemon thread when isDaemon
is true. This method throws java.lang.IllegalThreadStateException when the
thread has not yet been created and started.


void setName(String threadName)
Assigns threadName’s value to this Thread object as the name of its associated
thread.
static void sleep(long time)
Pauses the thread associated with this Thread object for time milliseconds. This
method throws InterruptedException when this Thread object’s interrupt()
method is called while the thread is sleeping.


void start() Creates and starts this Thread object’s associated thread. This method throws
IllegalThreadStateException when the thread was previously started and is
running or has died.

Listing 7-13 introduces you to the Threads API via a main() method that demonstrates Runnable,
Thread(Runnable runnable), currentThread(), getName(), and start().


Listing 7-13. A Pair of Counting Threads
public class CountingThreads
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
String name = Thread.currentThread().getName();
int count = 0;

while (true)
System.out.println(name + ": " + count++);
}
};
Thread thdA = new Thread(r);
Thread thdB = new Thread(r);
thdA.start();
thdB.start();
}
}

According to Listing 7-13, the default main thread that executes main() first instantiates an
anonymous class that implements Runnable. It then creates two Thread objects, initializing each
object to the runnable, and calls Thread’s start() method to create and start both threads. After
completing these tasks, the main thread exits main() and dies.

根据清单7-13,执行main()的默认线程首先实例化一个实现Runnable的匿名类。
Each of the two started threads executes the runnable’s run() method. It calls Thread’s
currentThread() method to obtain its associated Thread instance, uses this instance to call Thread’s
getName() method to return its name, initializes count to 0, and enters an infinite loop where it
outputs name and count, and increments count on each iteration.

Tip To stop an application that doesn’t end, press the Ctrl and C keys simultaneously on a Windows
platform or do the equivalent on a non-Windows platform.

I observed both threads alternating in their execution when I ran this application on the 64-bit
Windows 7 platform. Partial output from one run appears here:
Thread-0: 0
Thread-0: 1
Thread-1: 0
Thread-0: 2
Thread-1: 1
Thread-0: 3
Thread-1: 2
Thread-0: 4
Thread-1: 3
Thread-0: 5
Thread-1: 4
Thread-0: 6
Thread-1: 5
Thread-0: 7
Thread-1: 6
Thread-1: 7
Thread-1: 8
Thread-1: 9
Thread-1: 10
Thread-1: 11
Thread-1: 12

Note I executed java CountThreads >output.txt to capture the output to output.txt and then
presented part of this file’s content above. Capturing output to a file may significantly affect the output that
would otherwise be observed if output wasn’t captured. Because I present captured thread output throughout
this section, bear this in mind when executing the application on your platform. Also, note that your platform’s
threading architecture may impact the observable results. I've tested each thread example on the 64-bit
Windows 7 platform.


Also, although the output shows that the first thread (Thread-0) starts executing, never assume that the
thread associated with the Thread object whose start() method is called first will execute first.

When a computer has enough processors and/or processor cores, the computer’s operating system
assigns a separate thread to each processor or core so the threads execute simultaneously. When a
computer doesn’t have enough processors and/or cores, various threads must wait their turns to use
the shared processors/cores.

当计算机有足够多的处理器和/ 或处理器内核,计算机的操作系统分配单个线程到各个处理器或内核,这样线程同时执行。当计算机没有足够的处理器和/或内核,不同的线程必须等待它们使用处理器/内核的轮次。


The operating system uses a scheduler (http://en.wikipedia.org/wiki/Scheduling_(computing)) to
determine when a waiting thread executes. The following list identifies three different schedulers:
Linux 2.6 through 2.6.22 uses the    O(1) Scheduler ( http://en.wikipedia.org/
wiki/O(1)_scheduler).
Linux 2.6.23 uses the    Completely Fair Scheduler (http://en.wikipedia.org/
wiki/Completely_Fair_Scheduler).
Windows NT-based operating systems (NT, XP, Vista, and 7) use a    multilevel
feedback queue scheduler (http://en.wikipedia.org/wiki/Multilevel_
feedback_queue). This scheduler has been adjusted in Windows Vista and
Windows 7 to optimize performance.

A multilevel feedback queue and many other thread schedulers take priority (thread relative
importance) into account. They often combine preemptive scheduling (higher priority threads
preempt—interrupt and run instead of—lower priority threads) with round robin scheduling (equal
priority threads are given equal slices of time, which are known as time slices, and take turns
executing).

占先+时间分片

Note Two terms that are commonly encountered when exploring threads are parallelism and concurrency.
According to Oracle’s “Multithreading Guide” (http://docs.oracle.com/cd/E19455-01/806-
5257/6je9h032b/index.html), parallelism is “a condition that arises when at least two threads are
executing simultaneously.” In contrast, concurrency is “a condition that exists when at least two threads
are making progress. [It is a] more generalized form of parallelism that can include time-slicing as a
form of virtual parallelism.”

Thread supports priority via its void setPriority(int priority) method (set the priority of this
Thread object’s thread to priority, which ranges from Thread.MIN_PRIORITY to Thread.MAX_
PRIORITY—Thread.NORMAL_PRIORITY identifies the default priority) and int getPriority() method
(return the current priority).

Caution Using the setPriority() method can impact an application’s portability across platforms
because different schedulers can handle a priority change in different ways. For example, one platform’s
scheduler might delay lower priority threads from executing until higher priority threads finish. This delaying
can lead to indefinite postponement or starvation because lower priority threads “starve” while waiting
indefinitely for their turn to execute, and this can seriously hurt the application’s performance. Another
platform’s scheduler might not indefinitely delay lower priority threads, improving application performance.

可能影响可移植性。

Listing 7-14 refactors Listing 7-13’s main() method to give each thread a nondefault name and to
put each thread to sleep after outputting name and count.


Listing 7-14. A Pair of Counting Threads Revisited
public class CountingThreads
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
String name = Thread.currentThread().getName();
int count = 0;
while (true)
{
System.out.println(name + ": " + count++);
try
{
Thread.sleep(100);
}
catch (InterruptedException ie)
{
}
}
}
};
Thread thdA = new Thread(r);
thdA.setName("A");
Thread thdB = new Thread(r);
thdB.setName("B");

thdA.start();
thdB.start();
}
}
Listing 7-14 reveals that threads A and B execute Thread.sleep(100); to sleep for 100 milliseconds.
This sleep results in each thread executing more frequently, as the following partial output reveals:
A: 0
B: 0
A: 1
B: 1
B: 2
A: 2
B: 3
A: 3
B: 4
A: 4
B: 5
A: 5
B: 6
A: 6
B: 7
A: 7

A thread will occasionally start another thread to perform a lengthy calculation, download a large file,
or perform some other time-consuming activity. After finishing its other tasks, the thread that started
the worker thread is ready to process the results of the worker thread and waits for the worker thread
to finish and die.

一个线程偶尔会开启另一个线程来完成长时间的计算,...


It’s possible to wait for the worker thread to die by using a while loop that repeatedly calls Thread’s
isAlive() method on the worker thread’s Thread object and sleeps for a certain length of time when
this method returns true. However, Listing 7-15 demonstrates a less verbose alternative: the join()
method.

等待辅助线程死亡是可能的,通过使用一个当型循环重复调用辅助线程的Thread对象上的isAlive()方法并在这个方法返回true时睡眠一定时间。然而,清单7-15说明了一个少啰嗦的替代:join()方法。
Listing 7-15. Joining the Default Main Thread with a Background Thread

清单 7-15 连接默认主线程和背景线程

public class JoinDemo
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
System.out.println("Worker thread is simulating " +
"work by sleeping for 5 seconds.");  //通过睡眠5秒模拟辅助线程工作
try
{
Thread.sleep(5000);
}

catch (InterruptedException ie)
{
}
System.out.println("Worker thread is dying");
}
};
Thread thd = new Thread(r);
thd.start();
System.out.println("Default main thread is doing work.");
try
{
Thread.sleep(2000);
}
catch (InterruptedException ie)
{
}
System.out.println("Default main thread has finished its work.");
System.out.println("Default main thread is waiting for worker thread " +
"to die.");
try
{
thd.join();
}
catch (InterruptedException ie)
{
}
System.out.println("Main thread is dying");
}
}
Listing 7-15 demonstrates the default main thread starting a worker thread, performing some work,
and then waiting for the worker thread to die by calling join() via the worker thread’s thd object.
When you run this application, you will discover output similar to the following (message order might
differ somewhat):
Default main thread is doing work.
Worker thread is simulating work by sleeping for 5 seconds.
Default main thread has finished its work.
Default main thread is waiting for worker thread to die.
Worker thread is dying
Main thread is dying
Every Thread object belongs to some ThreadGroup object; Thread declares a ThreadGroup
getThreadGroup() method that returns this object. You should ignore thread groups because they
are not that useful. If you need to logically group Thread objects, you should use an array or
collection instead.

每个Thread对象属于某个ThreadGroup 对象;

Caution Various ThreadGroup methods are flawed. For example, int enumerate(Thread[]
threads) will not include all active threads in its enumeration when its threads array argument is too
small to store their Thread objects. Although you might think that you could use the return value from the
int activeCount() method to properly size this array, there is no guarantee that the array will be large
enough because activeCount()’s return value fluctuates with the creation and death of threads.

警告 各种各样的ThreadGroup 方法是有缺陷的。

However, you should still know about ThreadGroup because of its contribution in handling exceptions
that are thrown while a thread is executing. Listing 7-16 sets the stage for learning about exception
handling by presenting a run() method that attempts to divide an integer by 0, which results in a
thrown ArithmeticException instance.
Listing 7-16. Throwing an Exception from the run() Method
public class ExceptionThread
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
int x = 1 / 0; // Line 10
}
};
Thread thd = new Thread(r);
thd.start();
}
}
Run this application and you will see an exception trace that identifies the thrown
ArithmeticException.
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at ExceptionThread$1.run(ExceptionThread.java:10)
at java.lang.Thread.run(Unknown Source)
When an exception is thrown out of the run() method, the thread terminates and the following
activities take place:

当从run()方法抛出一个异常时,线程终止并且产生下列动作:

The virtual machine looks for an instance of    Thread.UncaughtExceptionHandler
installed via Thread’s void setUncaughtExceptionHandler(Thread.
UncaughtExceptionHandler eh) method. When this handler is found, it passes
execution to the instance’s void uncaughtException(Thread t, Throwable
e) method, where t identifies the Thread object of the thread that threw the
exception, and e identifies the thrown exception or error—perhaps a java.lang.
OutOfMemoryError instance was thrown. If this method throws an exception/
error, the exception/error is ignored by the virtual machine.

虚拟机寻找经由Thread的setUncaughtExceptionHandler(Thread.
UncaughtExceptionHandler eh) 方法安装的Thread.UncaughtExceptionHandler。

    Assuming that    setUncaughtExceptionHandler() was not called to install a
handler, the virtual machine passes control to the associated ThreadGroup
object’s uncaughtException(Thread t, Throwable e) method.Assuming that
ThreadGroup was not extended and that its uncaughtException() method was
not overridden to handle the exception, uncaughtException() passes control
to the parent ThreadGroup object’s uncaughtException() method when a
parent ThreadGroup is present. Otherwise, it checks to see if a default uncaught
exception handler has been installed (via Thread’s static void setDefaultUncau
ghtExceptionHandler(Thread.UncaughtExceptionHandler handler) method). If a
default uncaught exception handler has been installed, its uncaughtException()
method is called with the same two arguments. Otherwise, uncaughtException()
checks its Throwable argument to determine if it is an instance of java.lang.
ThreadDeath. If so, nothing special is done. Otherwise, as Listing 7-16’s
exception message shows, a message containing the thread’s name, as
returned from the thread’s getName() method, and a stack backtrace, using the
Throwable argument’s printStackTrace() method, is printed to the standard
error stream.

假设setUncaughtExceptionHandler() 没被调用以安装一个处理器,虚拟机传递控制给相关的ThreadGroup对象的uncaughtException(Thread t, Throwable e) 方法。


Listing 7-17 demonstrates Thread’s setUncaughtExceptionHandler() and
setDefaultUncaughtExceptionHandler() methods.
Listing 7-17. Demonstrating Uncaught Exception Handlers
public class ExceptionThread
{
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
int x = 1 / 0;
}
};
Thread thd = new Thread(r);
Thread.UncaughtExceptionHandler uceh;
uceh = new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
System.out.println("Caught throwable " + e + " for thread "
+ t);
}
};
thd.setUncaughtExceptionHandler(uceh);
uceh = new Thread.UncaughtExceptionHandler()

When you run this application, you will observe the following output:
Caught throwable java.lang.ArithmeticException: / by zero for thread Thread[Thread-0,5,main]
You will not also see the default uncaught exception handler’s output because the default handler is
not called. To see that output, you must comment out thd.setUncaughtExceptionHandler(uceh);. If
you also comment out thd.setDefaultUncaughtExceptionHandler(uceh);, you will see Listing 7-16’s
output.

Caution Thread declares several deprecated methods, including stop() (stop an executing thread). These
methods have been deprecated because they are unsafe. Do not use these deprecated methods. (I will show
you how to safely stop a thread later in this chapter.) Also, you should avoid the static void yield()
method, which is intended to switch execution from the current thread to another thread, because it can
affect portability and hurt application performance. Although yield() might switch to another thread on
some platforms (which can improve performance), yield() might only return to the current thread on other
platforms (which hurts performance because the yield() call has only wasted time







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值