进程和线程
进程是计算机系统中能独立运行并作为资源分配的基本单位,它是由PCB(进程控制块),数据段和代码段组成,是一个能独立运行的基本单位.进程的创建,调度,分派都需要较大的时间和空间开销,在操作系统中引入线程,以线程作为调度和分派的基本单位,以此来改进多处理机系统的性能
线程是操作系统能够进行运算调度的最小单位,同一个进程中的多条线程共享该进程中的全部系统资源
进程与线程的区别:
- 进程每次调度时,都需要进行上下文切换将断点的信息保存在PCB中,开销较大.线程调度时,只需要保存和设置少量的寄存器内容,切换代价比较小
- 进程作为系统资源分配的基本单位,拥有一定的资源,包括用于存放程序,数据的磁盘和内存地址空间,以及在它运行时所需要的I/O设备,已打开的信号量等.而线程只拥有一点必不可少的,能保证独立运行的资源(如控制线程运行的TCB,用于指示被执行指令序列的程序计数器,保存局部变量,少数状态参数和返回地址等的一组寄存器和堆栈),允许多个线程共享该进程中的内存地址空间和资源
java中实现线程的两种方式
java中允许程序并发地运行多个线程,并发是指同一时间间隔中有两个或多个事件同时发生,并行是指同一时刻有两个或多个事件同时执行.
1. 继承Thread类并重写run()方法
Thread表示线程类,run方法中是我们启动线程时要做的事,应该使用start()方法启动线程,直接调用run方法相当于普通的方法调用,下面我们通过继承Thread类的方式定义了一个线程类用来输出0到9,接着在main方法中创建两个线程类的实例并启动线程,可以看出线程的执行情况是随机的.线程的调度细节取决于操作系统,抢占式的线程调度给每个线程一个时间片的时间运行该线程,当该时间片完时,系统会保存当前线程的状态,然后终止当前线程的执行,执行另一个线程,当下一个时间片完时,又会终止当前执行线程,依次类推.直到所有线程终止.
1 public class FirstThread extends Thread{ 2 @Override 3 public void run() { 4 for(int i = 0;i<10;i++) { 5 System.out.println(Thread.currentThread().getName()+"打印"+i); 6 } 7 } 8 } 9 public class FirstDemo {10 public static void main(String[] args) {11 Thread thread = new FirstThread();12 Thread thread2 = new FirstThread();13 thread.start();14 thread2.start();15 } 16 }
![353c3f5a8f98f61afda4c69921202b37.png](https://i-blog.csdnimg.cn/blog_migrate/6d442db297499a2fbec9819cfcf421cd.jpeg)
2.实现Runnable接口
Thread类有一个参数为Runnable对象的构造函数,它可以使用给定的Runnable对象创建一个线程.
1 public class FirstThread implements Runnable{ 2 @Override 3 public void run() { 4 for(int i = 0;i<10;i++) { 5 System.out.println(Thread.currentThread().getName()+"打印"+i); 6 } 7 } 8 } 9 public class FirstDemo {10 public static void main(String[] args) {11 Thread thread = new Thread(new FirstThread());12 Thread thread2 = new Thread(new FirstThread());13 thread.start();14 thread2.start();15 } 16 }
线程属性
线程名(默认Thread-线程初始化时的序号)
1 private String name; 2 3 // 设置线程的名字 4 public final synchronized void setName(String name) { 5 checkAccess(); // 检查是否允许更改线程的参数 6 7 if (name == null) { 8 throw new NullPointerException("name cannot be null"); 9 }10 11 this.name = name;12 if (threadStatus != 0) { // 判断线程的状态13 setNativeName(name);14 }15 } 16 17 // 获取线程的名字18 public final String getName() {19 return name;20 }
线程的优先级(默认是父线程的优先级)
1 private int priority; 2 3 public final static int MIN_PRIORITY = 1; // 线程的最小优先级 4 5 public final static int NORM_PRIORITY = 5; // 线程的默认优先级 6 7 public final static int MAX_PRIORITY = 10; // 线程的最大优先级 8 9 // 设置线程的优先级10 public final void setPriority(int newPriority) {11 ThreadGroup g;12 checkAccess(); // 安全检查13 14 // 检查设定的优先级是否大于最大优先级或最小优先级,如果是抛出参数不合法异常 15 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {16 throw new IllegalArgumentException();17 }18 19 // 获取当前的线程组20 if((g = getThreadGroup()) != null) {21 // 如果当前设定的优先级大于它所在线程组的最大优先级22 if (newPriority > g.getMaxPriority()) {23 // 设置当前线程的优先级为当前线程组的最大优先级24 newPriority = g.getMaxPriority();25 }26 // 调用本地方法setPriority0设置线程优先级27 setPriority0(priority = newPriority);28 }29 }30 31 // 获取当前线程的优先级32 public final int getPriority() {33 return priority;34 }35
线程是否是守护线程(默认是父线程的值)
守护线程是为其他线程提供服务的线程,当一个程序只剩下守护线程了,java虚拟机就会退出程序,也就是说守护线程是依托用户线程而存在的,用户线程的终止将会导致守护线程终止,不管当前处于什么状态,在使用守护线程时,应该避免守护线程去访问固有的资源,如文件,数据库等.它会在任何一个操作的中间发生中断.
1 private boolean daemon; 2 3 // 设置该线程是否是守护线程 4 public final void setDaemon(boolean on) { 5 // 安全检查 6 checkAccess(); 7 8 // 如果线程处于活动状态,抛出表示没有在线程适当状态操作进行的异常 9 if (isAlive()) {10 throw new IllegalThreadStateException();11 }12 // 设置线程是否为守护线程13 daemon = on;14 }15 // 判断当前线程是否处于活动状态16 public final native boolean isAlive();17 18 // 判断当前线程是否是 守护线程19 public final boolean isDaemon() {20 return daemon;21 }
线程的状态
线程的状态使用一个内部静态枚举类表示
1 public enum State { 2 3 NEW, // 新建状态 4 5 RUNNABLE, // 可运行状态 6 7 BLOCKED, // 阻塞状态 8 9 WAITING, // 等待状态10 11 TIMED_WAITING, // 等待一段时间的状态12 13 TERMINATED; // 终止状态14 }15 16 // 获取当前线程的状态17 public State getState() {18 // get current thread state19 return sun.misc.VM.toThreadState(threadStatus);20 }
线程的未捕获异常处理器
线程的run方法没有抛出任何的受查异常,如果在运行时抛出异常,然而并未捕获处理该异常,该线程即将终止,此时就会将该线程和异常作为参数调用未捕获异常处理器的方法,未捕获异常处理器是一个实现UncaughtExceptionHandler接口的类,UncaughtException是Thread的一个静态内部类,它的内部只有一个方法 voiduncaughtException(Thread t, Throwable e); 该方法将会在线程因未捕获的异常而终止时调用.ThreadGroup类实现了该接口.Thread类有两个UncaughtExceptionHandler接口的变量,一个是默认的处理器(用static修饰),供所有的线程使用,另一个只供当前线程使用.默认情况下这两个变量都为null,线程使用的未捕获异常处理器是该线程组的处理器.
1 // 供当前线程使用 2 private volatile UncaughtExceptionHandler uncaughtExceptionHandler; 3 4 // 获取当前线程的未捕获异常处理器 5 public UncaughtExceptionHandler getUncaughtExceptionHandler() { 6 // 如果当前线程无未捕获异常处理器,则返回该线程所属线程组的处理器 7 return uncaughtExceptionHandler != null ? 8 uncaughtExceptionHandler : group; 9 }10 // 设置当前线程的未捕获异常处理器11 public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {12 checkAccess();13 uncaughtExceptionHandler = eh;14 }15 16 // 供所有线程使用17 private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;18 19 // 获取默认的未捕获异常处理器20 public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){21 return defaultUncaughtExceptionHandler;22 }23 // 设置默认的未捕获异常处理器24 public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {25 26 // 获取系统安全管理器27 SecurityManager sm = System.getSecurityManager();28 if (sm != null) {29 // 检查是否允许执行给定权限的请求,如果不允许,抛出SecurityException异常30 sm.checkPermission(31 32 // 创建一个在线程由于未捕获的异常而突然终止时,设置将要使用的默认处理程序的运行时权限对象33 new RuntimePermission("setDefaultUncaughtExceptionHandler")34 );35 }36 // 设置默认的未捕获异常处理器 37 defaultUncaughtExceptionHandler = eh;38 }
1 // ThreadGroup 实现Thread.Uncaughtexception接口的源码解析 2 public void uncaughtException(Thread t, Throwable e) { 3 // 如果该线程组有父线程组,则使用父线程组的处理器 4 if (parent != null) { 5 parent.uncaughtException(t, e); 6 } else { 7 8 // 获取默认的未捕获异常处理器 9 Thread.UncaughtExceptionHandler ueh =10 Thread.getDefaultUncaughtExceptionHandler();11 12 // 如果有默认的异常处理器就使用它13 if (ueh != null) {14 ueh.uncaughtException(t, e);15 16 } 17 // 否则判断引起线程终止的异常是否是ThreadDeath的子类,如果是,什么也不做,负责将线程名和堆栈信息输出到标准错误流中,ThreadDeath是18 else if (!(e instanceof ThreadDeath)) {19 System.err.print("Exception in thread ""20 + t.getName() + "" ");21 e.printStackTrace(System.err);22 }23 }24 }
线程组ThreadGroup
线程组是一个统一管理线程的集合,除了初始线程组,每个线程组有一个父线程组,线程组也可以包含其他线程组,允许线程访问有关自己的线程组的信息,但是不允许它访问有关其线程组的父线程组或其他任何线程组的信息.默认情况下,创建的线程组都属于main线程组.线程组有两个构造方法
线程组属性
1 private final ThreadGroup parent; // 父线程组,一旦指定不可修改 2 String name; // 线程组名 3 int maxPriority; // 线程组的最大优先级 4 boolean destroyed; // 线程组是否被销毁 5 boolean daemon; // 线程组是否是守护线程组 6 boolean vmAllowSuspension; // 虚拟机自动挂起 7 8 int nUnstartedThreads = 0; // 线程组中未启动线程的数量 9 int nthreads; // 此线程组包含的线程数10 Thread[] threads[]; // 该线程组中包含的线程11 12 int ngroups; // 子线程组计数器13 ThreadGroup[] groups; // 该线程组中包含的线程组
1 构造函数调用顺序从上到下 2 3 //使用指定的线程组名构造线程组 4 public ThreadGroup(String name) { 5 // 获取当前线程组的父线程组 6 this(Thread.currentThread().getThreadGroup(), name); 7 } 8 9 //创建一个父线程组时指定线程,并且名字为name的线程组10 public ThreadGroup(ThreadGroup parent, String name) {11 // 检查父线程组12 this(checkParentAccess(parent), parent, name);13 }14 15 16 //检查线程组的安全17 private static Void checkParentAccess(ThreadGroup parent) {18 parent.checkAccess();19 return null;20 }21 //安全检查22 public final void checkAccess() {23 SecurityManager security = System.getSecurityManager();24 if (security != null) {25 security.checkAccess(this);26 }27 }28 29 private ThreadGroup(Void unused, ThreadGroup parent, String name) {30 // 设置线程组名31 this.name = name;32 // 最大优先级是父线程组的最大优先级 33 this.maxPriority = parent.maxPriority;34 // 是否是守护线程组合父线程组保持一致35 this.daemon = parent.daemon;36 // 虚拟机参数37 this.vmAllowSuspension = parent.vmAllowSuspension;38 this.parent = parent;39 parent.add(this);40 }41 // 添加指定的线程组42 private final void add(ThreadGroup g) {43 synchronized (this) {44 // 如果线程组已经被销毁,抛出异常45 if (destroyed) {46 throw new IllegalThreadStateException();47 }48 // 判断此线程组子线程组数组是否为空或已满49 // 如果为空,创建子线程数组,初始容量为4,将指定线程组加入该数组,计数器加一;50 // 如果子线程数组已满,数组容量扩容两倍,将指定线程组存入数组,计数器加一51 // 如果子线程数组未满,将指定线程组加入该数组,计数器加一52 if (groups == null) {53 groups = new ThreadGroup[4];54 } else if (ngroups == groups.length) {55 groups = Arrays.copyOf(groups, ngroups * 2);56 }57 groups[ngroups] = g;58 ngroups++;59 }60 }
其他方法
// 移除指定的线程组
private void remove(ThreadGroup g) {
synchronized (this) {
// 判断该线程组是否死亡
if (destroyed) {
return;
}
// 循环遍历子线程组数组,找到要删除的线程组,如果找到,子线程组计数器减一
// 将处于该线程组元素后的元素向前移动,将最后一个元素置空,跳出循环
for (int i = 0; i < ngroups; i++) {
if (groups[i] == g) {
ngroups -= 1;
System.arraycopy(groups, i + 1, groups, i, ngroups - i);
groups[ngroups] = null;
break;
}
}
// 唤醒在此对象上所有等待的线程
if (nthreads == 0) {
notifyAll();
}
// 如果此线程组同时满足以下条件,销毁该线程组及其子组
// 1. 是守护线程组
// 2. 包含的线程数为0
// 3. 未启动线程数量为0
// 4. 包含的线程组数为0
if (daemon && (nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) {
destroy();
}
}
}
// 将指定线程加入该线程组
void add(Thread t) {
synchronized (this) {
if (destroyed) {
throw new IllegalThreadStateException();
}
if (threads == null) {
threads = new Thread[4];
} else if (nthreads == threads.length) {
threads = Arrays.copyOf(threads, nthreads * 2);
}
threads[nthreads] = t;
nthreads++;
// 为了防止线程组被销毁,未启动线程数递减
nUnstartedThreads--;
}
}
// 移除指定的线程
private void remove(Thread t) {
synchronized (this) {
if (destroyed) {
return;
}
for (int i = 0; i < nthreads; i++) {
if (threads[i] == t) {
System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
threads[nthreads] = null;
break;
}
}
}
}
// 获得该线程组的名字
public final String getName() {
return name;
}
// 获得该线程组的最大优先级
public final int getMaxPriority() {
return maxPriority;
}
// 获得该线程组的父线程组
public final ThreadGroup getParent() {
if (parent != null)
parent.checkAccess();
return parent;
}
// 判断该线程组是否是守护线程组
public final boolean isDaemon() {
return daemon;
}
// 判断该线程组是否被销毁
public synchronized boolean isDestroyed() {
return destroyed;
}
// 设置该线程组的最大优先级
public final void setMaxPriority(int pri) {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
// 判断给定优先级是否符合要求,不符合要求直接返回
if (pri < Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY) {
return;
}
// 如果当前线程组没有父线程组,最大优先级就为给定的值
// 否则判断父线程组最大优先级和指定优先级,取最小值为该线程组的最大优先级
maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri;
// 获取该线程子线程组的快照(如果存在的话
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
// 遍历该线程组的所有子线程组,递归将最大优先级设置为指定值
for (int i = 0; i < ngroupsSnapshot; i++) {
groupsSnapshot[i].setMaxPriority(pri);
}
}
// 设置该线程组是否是守护线程
public final void setDaemon(boolean daemon) {
checkAccess();
this.daemon = daemon;
}
// 测试此线程组是否是指定线程组的祖先
public final boolean parentOf(ThreadGroup g) {
for (; g != null; g = g.parent) {
if (g == this) {
return true;
}
}
return false;
}
// 销毁此线程组及其子组
public final void destroy() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
// 如果线程组已经销毁或该线程组还有活动的线程,抛出一个异常
if (destroyed || (nthreads > 0)) {
throw new IllegalThreadStateException();
}
// 获取子线程组的快照(如果存在的话)
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
// 如果该线程组存在父线程组,将各种属性置空
if (parent != null) {
destroyed = true;
ngroups = 0;
groups = null;
nthreads = 0;
threads = null;
}
}
// 循环遍历子线程组,对每个子线程组进行递归处理
for (int i = 0; i < ngroupsSnapshot; i += 1) {
groupsSnapshot[i].destroy();
}
// 如果一个线程组存在父线程组,从父线程组中移除该线程组
if (parent != null) {
parent.remove(this);
}
}
// 获得此线程组中活动的线程数
public int activeCount() {
int result;
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
result = nthreads;
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0; i < ngroupsSnapshot; i++) {
result += groupsSnapshot[i].activeCount();
}
return result;
}
// 获得此线程组中活动的线程组数
public int activeGroupCount() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
int n = ngroupsSnapshot;
for (int i = 0; i < ngroupsSnapshot; i++) {
n += groupsSnapshot[i].activeGroupCount();
}
return n;
}
// 将此线程组的有关信息输出到标准输出流
public void list() {
list(System.out, 0);
}
void list(PrintStream out, int indent) {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
for (int j = 0; j < indent; j++) {
out.print(" ");
}
out.println(this);
indent += 4;
for (int i = 0; i < nthreads; i++) {
for (int j = 0; j < indent; j++) {
out.print(" ");
}
out.println(threads[i]);
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0; i < ngroupsSnapshot; i++) {
groupsSnapshot[i].list(out, indent);
}
}
// 返回此此类的字符串描述:类名+线程组名+最大优先级
public String toString() {
return getClass().getName() + "[name=" + getName() +