共计 3413 个字符,预计需要花费 9 分钟才能阅读完成。
前言和纲要
计算机系统里每个过程(Process)都代表着一个运行着的程序,过程是对运行时程序的封装,零碎进行资源调度和调配的根本单位。
一个过程下能够有很多个线程,线程是过程的子工作,是 CPU 调度和分派的根本单位,用于保障程序的实时性,实现过程外部的并发,线程同时也是操作系统可辨认的最小执行和调度单位。
在 Java 里线程是程序执行的载体,咱们写的代码就是由线程运行的。有的时候为了减少程序的执行效率,咱们不得不应用多线程进行编程,尽管多线程能最大化程序利用 CPU 的效率,但也是程序事变多发、程序员脱发的最大诱因。次要是平时咱们的思维默认是单线程的,写多线程的时候得能够切换一下才行,这就要求咱们对线程的基础知识理解的比拟透彻。
这篇文章咱们总结一下 Java 线程的根底,多把握点,当前就少掉点头发,不光省下植发的钱,工资还能往上涨,这么一想几乎双赢。本文的纲要如下:
好了让咱们开始明天的注释,间接上代码吧。
Java 中的线程
到目前为止,咱们写的所有 Java 程序代码都是在由 JVM 给创立的 Main Thread 中单线程里执行的。Java 线程就像一个虚构 CPU,能够在运行的 Java 应用程序中执行 Java 代码。当一个 Java 应用程序启动时,它的入口办法 main() 办法由主线程执行。主线程(Main Thread)是一个由 Java 虚拟机创立的运行你的应用程序的非凡线程。
因为 Java 里所有皆对象,所以线程也是用对象示意的,线程是类 java.lang.Thread 类或者其子类的实例。在 Java 应用程序外部,咱们能够通过 Thread 实例创立和启动更多线程,这些线程能够与主线程并行执行应用程序的代码。
创立和启动线程
在 Java 中创立一个线程,就是创立一个 Thread 类的实例
Thread thread = new Thread();
启动线程就是调用线程对象的 start() 办法
thread.start();
当然,这个例子没有指定线程要执行的代码,所以线程将在启动后立刻进行。
指定线程要执行的代码
有两种办法能够给线程指定要执行的代码。
第一种是,创立 Thread 的子类,笼罩父类的 run() 办法,在 run() 办法中指定线程要执行的代码。
第二种是,将实现 Runnable (java.lang.Runnable) 的对象传递给 Thread 构造方法,创立 Thread 实例。
其实,还有第三种用法,不过细究下来可归类到第二种的非凡应用形式,上面咱们看看这三种的用法和区别。
通过 Thread 子类指定要执行的代码
通过继承 Thread 类创立线程的步骤:
定义 Thread 类的子类,并笼罩该类的 run() 办法。run() 办法的办法体就代表了线程要实现的工作,因而把 run 办法称为执行体。
创立 Thread 子类的实例,即创立了线程对象。
调用线程对象的 start 办法来启动该线程。
package com.learnthread;
public class ThreadDemo {
public static void main(String[] args) {
// 实例化线程对象
MyThread threadA = new MyThread("Thread 线程 -A");
MyThread threadB = new MyThread("Thread 线程 -B");
// 启动线程
threadA.start();
threadB.start();}
static class MyThread extends Thread {
private int ticket = 5;
MyThread(String name) {super(name);
}
@Override
public void run() {while (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "张票");
ticket--;
}
}
}
}
下面的程序,主线程启动调用 A、B 两个线程的 start() 后,并没有通过 wait() 期待他们执行完结。A、B 两个线程的执行体,会并发地被零碎执行,等线程都间接完结后,程序才会退出。
通过实现 Runnable 接口指定要执行的代码
Runnable 接口里,只有一个 run() 办法的定义:
package java.lang;
public interface Runnable {
public abstract void run();
}
其实,Thread 类实现的也是 Runnable 接口。
在 Thread 类的重载构造方法里,反对接管一个实现了 Runnale 接口的对象作为其 target 参数来初始化线程对象。
public class Thread implements Runnable {
...
public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
}
...
public Thread(Runnable target, String name) {init(null, target, name, 0);
}
...
}
通过实现 Runnable 接口创立线程的步骤:
定义 Runnable 接口的实现,实现该接口的 run 办法。该 run 办法的办法体同样是线程的执行体。
创立 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创立 Thread 对象,该 Thread 对象才是真正的线程对象。
调用线程对象的 start 办法来启动线程并执行。
package com.learnthread;
public class RunnableDemo {
public static void main(String[] args) {
// 实例化线程对象
Thread threadA = new Thread(new MyThread(), "Runnable 线程 -A");
Thread threadB = new Thread(new MyThread(), "Runnable 线程 -B");
// 启动线程
threadA.start();
threadB.start();}
static class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {while (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "张票");
ticket--;
}
}
}
}
运行下面例程会有以下输入,同样程序会在所以线程执行完后退出。
Runnable 线程 -B 卖出了第 5 张票
Runnable 线程 -B 卖出了第 4 张票
Runnable 线程 -B 卖出了第 3 张票
Runnable 线程 -B 卖出了第 2 张票
Runnable 线程 -B 卖出了第 1 张票
Runnable 线程 -A 卖出了第 5 张票
Runnable 线程 -A 卖出了第 4 张票
Runnable 线程 -A 卖出了第 3 张票
Runnable 线程 -A 卖出了第 2 张票
Runnable 线程 -A 卖出了第 1 张票
Process finished with exit code 0
既然是给 Thread 传递 Runnable 接口的实现对象即可,那么除了一般的定义类实现接口的形式,咱们还能够应用匿名类和 Lambda 表达式的形式来定义 Runnable 的实现。
应用 Runnable 的匿名类作为参数创立 Thread 对象:
Thread threadA = new Thread(new Runnable() {
private int ticket = 5;
@Override
public void run() {while (ticket > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "张票");
ticket--;
}
}
}, “Runnable 线程 -A”);
应用实现了 Runnable 的 Lambda 表达式作为参数创立 Thread 对象:
Runnable runnable = () -> { System.out.println(“Lambda Runnable running”); };
Thread threadB = new Thread(runnable, “Runnable 线程 -B”);
因为,Lambda 是无状态的,定义不了外部属性,这里就举个简略的打印一行输入的例子了,了解一下这种用法即可。