Java 并 发 编 程 基 础
1. 概述
在支持同时运行多个任务的系统中,一个任务一般是一个程序,每个程序就是一个进程。而在一个程序其内部,可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
比如在Windows操作系统中,我们可以通过系统自带的任务管理器查看到进程列表。某些进程可以展开,可能会查看到一个或者多个运行的服务或子进程。
1.1 进程与线程
【进程】:进行中的程序。是内存正在执行中的应用程序及其某些必要资源总和,它是线程的容器。
- 程序进行起来他就成了进程
- 一个程序运行起来可能有多个进程:一种情况是,一个应用程序可以启动多个处理进程,所有进程隶属于当前应用程序即所谓的多进程服务。另一种情况是,一个程序只有一个进程,但开启了多个该程序。
进程一般有如下特征:
- 独立性:进程是系统中独立存在的实体。它可以拥有自己独立的资源。每个进行都有总计私有的地址空间。在没有经过进程允许时,一个用于进程不可以直接访问其它进程的地址空间。
- 动态性:程序知识一个静态的指令集合,而进程是一个正在系统中活动的指令集合。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
并发性:多个进程可以在单个处理器上并发执行,且它们之间不会互相影响。
【线程】:线程是位于进程中的执行单元,它是CPU调度的基本单元。
- 线程是操作系统分配CPU时间的基本实体
- 线程是处理器调度的最小单位。
1.2 并发与并行
【并发】:多个任务在CPU上"快速轮换”执行,而实际上任意时刻CPU只能执行其中一个任务。
【并行】:多CPU、多个任务真正同时地在执行。任意时刻多个CUP能同时执行多个任务,即并型才是真正地同时执行任务。
真正并行执行多任务只能在多核CPU上实现。但由于实践中任务数一般远远多余CPU的核心数,因此操作系统会把很多任务的轮流调度到多个核心上执行。对于用户来说,计算机扫描执行一轮又一轮,速度非常之快,直观感觉就像是在并行执行的了。 |
2. Java多线程技术
多线程扩展了多线程地概念,使得同一个进程可以同时并发处理多个任务。已经说过,线程(Thread)也称为轻量级进程(Lightweight Process),是进程地执行单元,而进程是线程地容器。
2.1 线程的生命周期
2.2 创建进程的三种方式
2.2.1 继承Tgread类
(1)继承Thread类,并重写run方法
run()
方法就是线程执行体,即该线程需要完成的事情。
(2)创建Thread子类的实例,并调用strat()
方法期待线程
2.2.2 实现Runnable接口
(1)实现Runnable接口,并重写run()方法;
(2)创建Runnable实现类的实例;
(3)以Runnable实现类的实例为target,创建Thread对象,再调用start方法启动线程。
2.2.3 实现Callable接口
创建Thread子类的实例并调用start()方法启动线程
2.3 多线程编程实例
【实例1】
附录 Thread类
thread是程序中的一个执行线程。Java虚拟机允许应用程序同时运行多个执行线程。
每个线程都有优先级。优先级较高的线程优先于优先级较低的线程执行。每个线程也可以标记为守护进程,也可以不标记。当在某个线程中运行的代码创建一个新的“线程”对象时,新线程的优先级最初被设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时,它才是守护进程。
当一个Java虚拟机启动时,通常有一个非守护线程(它通常调用某个指定类的名为“main”的方法)。Java虚拟机继续执行线程,直到出现以下任一情况:
- 类
Runtime
的exit
方法已被调用,安全管理器已允许退出操作发生。 - 所有不是守护线程的线程都已经死亡,要么是从调用
run
方法返回,要么是抛出一个传播到“运行”方法之外的异常。
有两种方法可以创建新的执行线程。一种是将一个类声明为 Thread
的子类。这个子类应该覆盖类 Thread
的run
方法。然后可以分配并启动子类的一个实例。例如,计算大于规定值的素数的线程可以写成如下形式:
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
下面的代码将创建一个线程并开始运行:
PrimeThread p = new PrimeThread(143); p.start();
创建线程的另一种方法是声明一个实现Runnable
接口的类。然后,该类实现“运行”方法。然后可以分配该类的一个实例,在创建 Thread
时作为参数传递,并启动。另一种样式中的相同示例如下所示:
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
下面的代码将创建一个线程并开始运行:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
出于识别目的,每个线程都有一个名称。多个线程可以具有相同的名称。如果在创建线程时没有指定名称,则会为其生成一个新名称。
除非另有说明,否则将“null”参数传递给此类中的构造函数或方法将导致引发NullPointRexception
。
构造器
构造器 | 说明 |
Thread() |
分配一个新的Thread 对象。 |
Thread(Runnable target) |
分配一个新的Thread 对象。 |
Thread(Runnable target, String name) |
分配一个新的Thread 对象。 |
Thread(String name) |
分配一个新的Thread 对象。 |
Thread(ThreadGroup group, Runnable target) |
分配一个新的Thread 对象。 |
Thread(ThreadGroup group, Runnable target, String name) |
分配一个新的Thread 对象,使其以 target 作为其运行对象,以指定的name 作为其名称,并属于group 所引用的线程组。 |
Thread(ThreadGroup group, Runnable target, String name, long stackSize) |
分配一个新的Thread 对象,使其以target 作为其运行对象,以指定的name 作为其名称,并属于 group 所指的线程组,并具有指定的堆栈大小。 |
Thread(ThreadGroup group, Runnable target, String name, long stackSize, boolean inheritThreadLocals) |
分配一个新的Thread 对象,使其以“目标”作为其运行对象,以指定的name 作为其名称,属于 group 引用的线程组,以指定的stackSize ,并继承inheritable thread-local变量的初始值,如果inheritThreadLocals 为true 。 |
Thread(ThreadGroup group, String name) |
分配一个新的Thread 对象。 |
Field
修饰符和类型 | 成员变量 | 描述 |
static int |
MAX_PRIORITY |
线程可以拥有的最大优先级 |
static int |
MIN_PRIORITY |
线程可以拥有的最低优先级 |
static int |
NORM_PRIORITY |
分配给线程的默认优先级 |
方法
修饰符和类型 | 方法 | 描述 |
static int |
activeCount() |
返回当前线程组及其子组中活动线程数量的估计值 |
void |
checkAccess() |
确定当前运行的线程是否有权修改此线程 |
protected Object |
clone() |
由于线程不能被有意义地克隆,因此引发CloneNotSupportedException (克隆支持异常) |
static Thread |
currentThread() |
返回对当前正在执行的线程对象的引用。 |
static void |
dumpStack() |
将当前线程的栈跟踪打印到标准错误流(standard error stream) |
static int |
enumerate(Thread[] tarray) |
将当前线程的线程组(thread group)及其子组(subgroups)中的每个活动线程复制到指定数组中 |
static Map<Thread,StackTraceElement[]> |
getAllStackTraces() |
Returns a map of stack traces for all live threads |
ClassLoader |
getContextClassLoader() |
返回此线程的上下文ClassLoader |
static Thread.UncaughtExceptionHandler |
getDefaultUncaughtExceptionHandler() |
返回线程因未捕获的异常而突然终止时调用的默认处理程序 |
long |
getId() |
返回此线程的标识符 |
String |
getName() |
返回该线程的名称 |
int |
getPriority() |
返回该线程的优先级 |
StackTraceElement[] |
getStackTrace() |
返回表示此线程堆栈转储的堆栈跟踪元素数组 |
Thread.State |
getState() |
返回此线程的状态 |
ThreadGroup |
getThreadGroup() |
返回该线程所属的线程组 |
Thread.UncaughtExceptionHandler |
getUncaughtExceptionHandler() |
返回当此线程由于未捕获的异常而突然终止时调用的处理程序 |
static boolean |
holdsLock(Object obj) |
当且仅当当前线程持有指定对象的监视器锁时,返回 true |
void |
interrupt() |
中断这个线程 |
static boolean |
interrupted() |
测试当前线程是否已被中断 |
boolean |
isAlive() |
测试该线程是否是活动的 |
boolean |
isDaemon() |
测试该线程是否是守护线程 |
boolean |
isInterrupted() |
测试该线程是否已被中断 |
void |
join() |
Waits for this thread to die |
void |
join(long millis) |
等待此线程死亡的时间最多为“毫秒(milliseconds )” |
void |
join(long millis, int nanos) |
等待此线程死亡的时间最多为“毫秒millis”加上“毫微秒nanos” |
static void |
onSpinWait() |
表示呼叫者暂时无法继续进行,直到其他活动发生一个或多个动作 |
void |
run() |
If this thread was constructed using a separate Runnable run object, then that Runnable object’s run method is called; otherwise, this method does nothing and returns |
void |
setContextClassLoader(ClassLoader cl) |
Sets the context ClassLoader for this Thread |
void |
setDaemon(boolean on) |
Marks this thread as either a daemon thread or a user thread |
static void |
setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) |
设置当线程由于未捕获的异常而突然终止时调用的默认处理程序,并且没有为该线程定义其他处理程序 |
void |
setName(String name) |
将此线程的名称更改为等于参数name |
void |
setPriority(int newPriority) |
更改此线程的优先级 |
void |
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) |
设置当此线程由于未捕获的异常而突然终止时调用的处理程序 |
static void |
sleep(long millis) |
根据系统计时器和调度程序的精度和准确度,使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数 |
static void |
sleep(long millis, int nanos) |
使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,这取决于系统计时器和调度程序的精度和准确度 |
void |
start() |
导致此线程开始执行;Java虚拟机调用这个线程的run 方法 |
String |
toString() |
返回此线程的字符串表示形式,包括线程的名称、优先级和线程组 |
static void |
yield() |
给调度程序的提示,当前线程愿意让出它当前对处理器的使用 |