线程的调度&&线程的生命周期&&Daemon线程
来源:互联网 发布:java page cache 编辑:程序博客网 时间:2024/06/05 06:04
线程的调度&&线程的生命周期&&Daemon线程
线程简介
什么是线程
线程是现代操作系统调度的最小单元,也叫做轻量级的进程。
在一个进程里面可以创建多个线程,这些线程都拥有各自的计数器,堆栈和局部变量等属性,并且可以访问共享的内存变量。
线程与进程
在引入线程的操作系统中,通常是把进程作为资源分配的基本单位。把线程作为独立运行和独立调度的基本单位。
线程与进程的区别:
- 进程间相互独立,同一进程的各个线程共享。某进程内的线程对于其他进程不可见。
进程间通过IPC通信,线程间可以直接读取进程的内存共享变量进程通信。
线程上下文切换比进程上下文切换要快的多。
线程的调度
线程调度模型
在计算机中,线程的调度有2中模型,分别是分时调度模型、抢占式调度模型。
分时调度模型:
分时调度模型是指让所有的线程轮流获得CPU的使用去权,并且平均分配每个线程占用CPU的时间片。
抢占式调度模型:
抢占式调度模式是指让可运行池中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个占用CPU,当它失去CPU的使用权后,在随机选择其他线程获取CPU使用权。
Java虚拟机默认采用抢占式调度模式。
线程的优先级
线程的优先级用1~10表示。数字越大优先级越高。在Thread
的类中提供对线程设置优先级的方法,同时还定义了3个静态常量。如下:
setPriority(int priority):设置线程优先级MAX_PRIORITY:优先级最高级别,相当于10。MIN_PRIORITY:优先级最低级别,相当于1NORM_PRIORITY:默认优先级,相当于5
注意:虽然Java提供了线程优先级的设置,但是不同的操作系统对线程的优先级支持是不一样的,不能很好的和Java中的线程一一对应。所以线程的优先级用来作为程序正确性的依赖。
线程休眠
public static native void sleep(long mills) throws InterruptedException;
如果我们想让线程休眠,我们可以在当前线程调用sleep(long mills)
方法,这个方法会抛出一个InterruptedException
异常。
需要注意的是,sleep(long mills)
是一个静态方法,只能控制当前正在运行的线程休眠,不能控制其他线程休眠,在休眠结束后,线程就会返回到就绪状态,而不是立即开始运行状态。
线程让步
public static native void yield();
线程让步可以通过yield()
方法来实现,该方法和sleep()
方法有些相似,都可以让当前正在运行的线程暂停,区别在于yield()
方法不会阻塞该线程,只是将线程转换成就绪状态,让系统的调度器重新调度一次。
线程插队
public final void join() throws InterruptedException{ this.join(0L); }public final synchronized void join(long var1) throws InterruptedException { long var3 = System.currentTimeMillis(); long var5 = 0L; if(var1 < 0L) { throw new IllegalArgumentException("timeout value is negative"); } else { if(var1 == 0L) { while(this.isAlive()) { this.wait(0L); } } else { while(this.isAlive()) { long var7 = var1 - var5; if(var7 <= 0L) { break; } this.wait(var7); var5 = System.currentTimeMillis() - var3; } } }}
在Thread
类中提供了一个join()
方法来实现线程插队的功能。当某个线程中调用其他线程的join()时,调用的线程将阻塞,直到被join()
加入的线程执行完成后才会继续运行。
线程的生命周期及状态
线程的整个生命周期可以分为5个阶段,分别是新建(创建)状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead)。
新建状态(New):创建线程对象后,该线程对象就处于新建状态了。
就绪状态(Runnable):当线程调用了start()方法后,它就进入了就绪状态。此时它只是具备了运行条件,能否获得CPU的使用权开始运行,还需要等待系统的调度。
运行状态(Running):处于就绪状态的线程获取CPU的使用权,开始执行
run()
方法,则该线程处于运行状态。当使用完CPU的使用权(时间片)时,系统会剥夺该线程占用的CPU资源,让其他线程获得机会。这时失去CPU的使用权的线程由运行状态变为就绪状态。阻塞状态(Blocked):一个正在运行的线程,在某些特殊情况下(下面列举),会放弃CPU的使用权,进入阻塞状态。线程进入阻塞状态后,就不能进入排队队列。只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
下面列举下线程由运行状态转换到阻塞状态的原因。以及如何从阻塞状态转为就绪状态:
当线程试图获取某个对象的同步锁时,如果该锁被其他线程锁持有,则当前线程就会进入阻塞状态,如果想从阻塞状态进入就绪状态必须获取到其他线程所持有的锁。
当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果想进入就绪状态就必须要等到这个阻塞的IO方法返回。
当线程调用
wait()
方法时,也会使线程进入阻塞状态,如果想进入就绪状态就需要使用notify()
方法唤醒该线程。- 当线程调用了Thread的
sleep()
方法时,也会进入阻塞状态,当时间设置休眠的时间到了,该线程就会进入就绪状态。 - 当在一个线程中调用了另外一个线程的join()方法时,会使当前线程进入阻塞状态,在这种情况下,需要等到新加入的线程运行结束后才会结束阻塞状态,进入就绪状态。
注意:线程进入阻塞状态后,需要进入就绪状态,等待排队,才能到运行状态。
- 死亡状态(Dead):当线程的
run()
方法正常执行完,或者线程抛出未捕获的异常或错误,线程进入死亡状态。一旦进入死亡状态,线程将不再有运行的资格。
线程的生命周期图:
守护线程(Daemon线程)
Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机中不存在飞Daemon的时候,Java虚拟机将会退出。
public final void setDaemon(boolean var1) { this.checkAccess(); if(this.isAlive()) { throw new IllegalThreadStateException(); } else { this.daemon = var1; }}
注意:Daemon属性需要在启动之前设置,不能再启动之后设置。
在start()方法之后,设置setDaemon()会报错,如:
at java.lang.Thread.setDaemon(Thread.java:1356) at com.example.DaemonDemo.main(DaemonDemo.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑,因为Java虚拟机退出时,Daemon线程中的finally块不一定执行。看下面例子:
public class DaemonDemo { public static void main(String[] args) { ThreadA thread1 = new ThreadA(); thread1.setName("thread1"); thread1.setDaemon(true); thread1.start(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName() + ": 执行结束"); } } public static class ThreadA extends Thread { @Override public void run() { System.out.println(getName() + ": 开始执行run()方法"); try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(getName() + ": 执行结束"); } } }}
最终结果打印:
thread1: 开始执行run()方法main: 执行结束
在main线程结束后,Java虚拟机退出了,Daemon线程的finally语句没有执行。
- 线程的调度&&线程的生命周期&&Daemon线程
- 线程--线程的生命周期
- Java的Daemon线程
- Java的Daemon线程
- 特殊的Daemon线程
- 线程的调度与控制 线程的生命周期
- Java(线程的生命周期及调度)
- 线程的调度-守护线程
- 线程的调度-守护线程
- Daemon线程与普通线程的区别
- daemon线程和user线程的区别
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- Java线程:线程的调度-守护线程
- 总结一个recyclerview使用模版
- 《Python数据分析与挖掘实战》笔记(三):数据探索
- 【BZOJ1833】【数位DP】 count 数字计数
- 两数组的交 II
- MySQL命令show full processlist
- 线程的调度&&线程的生命周期&&Daemon线程
- memset与memcpy的用法
- Maven从下载到eclipse建立项目
- JAVA---猜字游戏
- 暴力破解ssh远程登录
- mac终端下svn常用命令
- 计算机图形学
- selenium+robot+svn+jenkins自动化测试系列二:Jenkins配置自动化持续集成构建
- NAS资料收集