过程与线程
- 过程是程序向操作系统申请资源 (如内存空间和文件句柄) 的根本单位.
- 线程是过程中可独立执行的最小单位.
JAVA 线程 API
- 在 Java 中创立一个线程就是创立一个 Thread 类的实例。
- 每个线程都有其要执行的工作. 线程工作的解决逻辑是在 Thread 类的 run 办法中实现或进行调用的, 因而 run 办法相当于线程工作逻辑解决的入口办法, 它由 java 虚拟机在运行相应的线程时进行调用, 而不是由利用代码进行调用。
- Thread 类的 start 办法是用于启动相干线程的, 启动一个线程的本质是申请 java 虚拟机运行相应的线程, 而这个线程具体什么时候运行是由线程调度器决定的. 因而, 尽管 start 办法被执行了, 然而并不意味着这个线程就开始运行了, 它可能稍后运行, 也可能永远不运行.
-
Thread 中罕用的两个结构器是:Thread()和 Thread(Runnable target), 这两种创立线程的形式如下:
-
定义 Thread 类子类的形式创立线程
public class WelcomeApp {public static void main(String[] args) { // 创立线程 Thread welcomeThread = new WelcomeThread(); // 启动线程 welcomeThread.start(); // 输入“以后线程”的线程名称 System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName()); } } // 定义 Thread 类的子类 class WelcomeThread extends Thread { // 在该办法中实现线程的工作解决逻辑 @Override public void run() {System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName()); } }
-
实现 Runnable 接口的形式创立线程
public class WelcomeApp1 {public static void main(String[] args) { // 创立线程 Thread welcomeThread = new Thread(new WelcomeTask()); // 启动线程 welcomeThread.start(); // 输入“以后线程”的线程名称 System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName()); } } class WelcomeTask implements Runnable { // 在该办法中实现线程的工作解决逻辑 @Override public void run() { // 输入“以后线程”的线程名称 System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName()); } }
-
- 不论应用以上哪种形式运行, 一旦线程的 run 办法执行完结, 相应线程的运行也就完结了, 运行完结的线程所占的资源会如同其它 java 对象一样被 java 虚拟机垃圾回收。
- Thread 的实例只能 start 一次, 若屡次调用一个实例的
start()
会抛出IllegalThreadStateException
异样。 -
能够通过 Thread.currentThread()获取以后线程,进而能够对其进行属性设置或获取它的相干信息,例如:
Thread.currentThread().setName("线程 A"); Thread.currentThread().getName();
-
上述中,线程的 run 办法个别由 java 虚拟机调用,然而,线程也是一个 Thread 类的一个实例其次 run 办法也是由 public 修饰符润饰,所以 run 办法也能被间接调用,然而个别不会这么做,违反咱们床架线程的初衷。
public class WelcomeApp {public static void main(String[] args) { // 创立线程 Thread welcomeThread = new WelcomeThread(); // 启动线程 welcomeThread.start(); // 输入“以后线程”的线程名称 System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName()); welcomeThread.run();} } // 定义 Thread 类的子类 class WelcomeThread extends Thread { // 在该办法中实现线程的工作解决逻辑 @Override public void run() {System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName()); } } ================== 后果 ================== 1.Welcome! I'm main. 2.Welcome! I'm Thread-0. 2.Welcome! I'm main.
-
线程的属性
属性 属性类型及用处 只读 重要注意事项 编号
ID用于标识不同的线程, 不同的线程领有不同的编号 是 某个编号的线程运行完结后,该编号可能被后续创立的线程应用。不同线程领有的编号尽管不同,然而这种编号的唯一性只在 Java 虚拟机的一次运行无效。也就是说重启一个 Java 虚拟机(如重启 Web 服务器)后,某些线程的编号可能与上次 Java 虚拟机运行的某个线程的编号一样,因而该属性的值不适宜用作某种惟一标识,特地是作为数据库中的惟一标识(如主键) 名称
Name用于辨别不同的线程。默认值与线程的编号无关,默认值的格局为:Thread- 线程编号,如 Thread-0 否 Java 并不禁止咱们将不同的线程的名称属性设置为雷同的值。尽管如此,设置线程的名称属性有助于代码调试和问题定位 线程类别
Daemon值为 true 示意相应的线程为守护线程,否则示意相应的线程为用户线程。该属性的默认值与相应线程的父线程的该属性的值雷同,在失常进行 java 程序时,当有用户线程还没执行完虚拟机不会立刻进行,会期待其执行结束,然而如果只有守护线程还没执行完则不会阻止虚拟机进行,这阐明守护线程通常用于执行一些重要性不是很高的工作,例如监督其它线程的 否 该属性必须在相应线程启动之前设置,即对 setDaemon 办法的调用必须在对 start 办法的调用之前,否则 setDaemon 办法会抛出 IllegalThreadStateException 异样。负责一些要害工作解决的线程不合适设置为守护线程 优先级
Priority该属性实质上是给线程调度器的提醒,用于示意应用程序心愿哪个线程可能优先得以运行。Java 定义了 1~10 的 10 个优先级。默认值个别为 5。对于具体的一个线程而言,其优先级的默认值与其父线程(创立该线程的线程)的优先级值相等 否 个别应用默认优先级即可。不恰当地设置该属性值可能导致重大的问题(线程饥饿) -
线程办法
办法 性能 备注 static Thread
currentThread()返回以后线程,即以后代码的执行线程(对象) 同一段代码对 Thread.currentThread()的调用,其返回值可能对应着不同的线程(对象) void run() 用于实现线程的工作解决逻辑 该办法是由 Java 虚拟机间接调用的,个别状况下应用程序不应该调用该办法 void start() 启动相应线程 该办法的返回并不代表相应的线程曾经被启动。一个 Thread 实例的 start 办法只可能被调用一次,屡次调用会导致异样的抛出 void join() 期待相应线程运行完结 若线程 A 调用线程 B 的 join 办法,那么线程 A 的运行会被暂停,直到线程 B 运行完结 static void yield() 使以后线程被动放弃其对处理器的占用,这可能导致以后线程被暂停 这个办法是不牢靠的。该办法被调用时以后线程可能依然持续运行(视零碎以后的运行状况而定) static void sleep(long millis) 使以后线程休眠(暂停运行)指定的工夫
Thread 和 Runnable 的关系
-
Thread 类实现 Runnable 接口
public class Thread implements Runnable{}
- Runnable 是一个 interface 且其中只有一个办法,所以实现 Runnable 的类要从新 run 办法。
-
两种形式执行工作逻辑的过程
-
实现 Runnable 接口,重写 run 办法
class WelcomeTask implements Runnable { @Override public void run() {// 工作逻辑} }
new Thread(new WelcomeTask()).start();
WelcomeTask
实例在 Thread 中的传递过程,它最终会被赋值给 Thread 中的一个 target 成员变量private Runnable target;
具体过程如下:
1. 有参构造函数, 生成一个 ThreadName,例如:Thread-0public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0); }
2. 进入第一个初始化函数, 这外面无需关注其它参数,因为还有其它构造方法会调用 init(…)
private void init(ThreadGroup g, Runnable target, String name,long stackSize){init(g, target, name, stackSize, null, true); }
3. 第二层初始化函数,行将 target 赋值
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {if (name == null) {throw new NullPointerException("name cannot be null"); } this.name = name; ..... this.target = target; .... }
4.Thread 实例调用 start()办法
public synchronized void start() {//threadStatus != 0 代表这个 Thread 实例曾经被 start()过了, 所以会抛出异样 if (threadStatus != 0) throw new IllegalThreadStateException(); ... try {// 最终会调用本地办法 start0()来启动这个线程 start0(); ... } finally {...} }
5. 本地办法启动线程,行将线程交给虚拟机的线程调度器
private native void start0();
6. 线程调度器执行线程工作逻辑
// 启动线程后,调度器会帮忙咱们运行执行线程 run()办法, 即工作逻辑 // 如果 target != null 的时候会调用 WelcomeTask 中实现的工作逻辑, 否则什么都不会执行 public void run() {if (target != null) {target.run(); } }
-
继承 Thread 类,重写 run 办法
class WelcomeThread extends Thread { @Override public void run() {// 工作逻辑} }
new WelcomeThread().start(); ... // 任务调度器间接调用 WelcomeThread 中实现的工作逻辑
-
-
两种形式的区别
- 面向对象角度: 一种是基于继承实现
extends Thread
,一种是基于组合new Thread(new Runable())
, 组合绝对与继承来说耦合性更低。 -
对象共享的角度: 一个
CountingTask
实例能够被多个线程共享所以可能呈现资源竞争问题。class CountingThread extends Thread { int count = 0; @Override public void run() {for (int i = 0; i < 100; i++) {i++;} } System.out.println("count:" + count); } class CountingTask implments Runnable { int count = 0; @Override public void run() {for (int i = 0; i < 100; i++) {i++;} } System.out.println("count:" + count); } public static void main(String[] args) { Thread thread; CountingTask task = new CountingTask(); for(int i = 0;i < 10;i++){thread = new Thread(task); thread.start();} for(int i = 0;i < 10;i++){thread = new CountingThread(); thread.start();} }
- 面向对象角度: 一种是基于继承实现
线程的生命周期
- New(新创建)
Thread thread = new Thread();
此时线程只是备 new 进去并没有开始执行。 - Runnable(可运行)
thread.start();
1.ready: 还没有被分配资源。
2.running: 正在执行。 -
阻塞
1.Blocked(阻塞)没有获取被 synchronized 爱护的代码的锁。
2.Waiting(期待)1.Object.wait(); - o.notify()/o.notifyAll() 2.Thread.join(); - join 线程完结 / 被中断 Blocked 与 Waiting 的区别是 Blocked 是在期待开释某个资源,Waiting 是在期待达到某个条件。
-
Time Waiting(计时期待)设置了工夫参数的一些阻塞。
1.Thread.sleep(m);- 工夫超时 /join 的过程完结 / 被中断。
2.Object.wait(m)
- 工夫到 /o.notify()/o.notifyAll()
3.Thread.join(m)
- 工夫超时 /join 的过程完结 / 被中断。
- Terminated(终止)
1.run 办法失常执行结束。
2. 呈现没有捕捉的异样,意外终止。