Java 线程是一种在一个程序上同时运行多个进程的机制。它也是一个术语,指的是java.lang.Thread类,用于在Java中使用线程。
嗯,很多工作,如果人们分工,可以很快完成。通过为每个任务分配人员,甚至可以同时完成不同的任务。当然,这比单独工作更有效率。
同样的,如果你在一个程序中同时运行多个进程,你可以在短时间内高效地处理它们。机制是线程。线程现在很常见。
本文将从思想的基础和Java中如何使用线程,使用线程时应该注意的事项,以及与线程相关的话题,重点为初学者讲解。
本文基于Java 13的语言规范和API。该示例已确认可在Java 13环境中工作。
1. 1. 线程的基本思想和用法
让我们快速浏览一下线程并体验Java线程。在Java中,创建和运行线程非常容易。 你所要做的就是像普通方法一样创建你想在线程中运行的进程,并要求java.lang.Thread “运行这个进程”。
1-1 线程思维
线程允许程序根据程序员的需要增加执行处理的单元数。
线程主要用于最大化计算机的处理能力(尤其是多核和多处理器) 。例如,程序中线程的使用如下。
- 同时做不同的事情:一个线程可以与网络通信,而另一个线程可以处理用户执行的屏幕操作。
- 减少处理时间:将大问题分成可以同时处理的小问题,在线程中同时处理小问题,以减少整体时间。
- 增加单位时间的处理量:如果你的计算机具备同时运行线程的能力,可以增加单位时间的处理量。
如果您想到您周围的软件,您可能会认为您正在同时运行多个进程。现在使用线程在Web浏览器中运行任何东西已经司空见惯。
1-1-1 如果您将线程与便利店进行比较
如果您想到程序之外的线程,请想到便利店。尤其是喜欢中午人满为患的便利店。如果便利店只有一台收银台,可以立即排队结账。但是,如果您有多个收银员,您可以同时进行多次付款,排队时间会更短。此外,如果您有多个店员,您可以同时做不同的工作(检查、拾取物品、清洁等)。
如果便利店是一个程序,那么店员就是一个线程。它们的共同点是它们都是做一些工作或处理的单位。并且虽然不能立即增加业务员的数量,但程序可以根据需要快速创建和移动线程。
1-2 继承 Thread 类并尝试使用它
现在让我们在Java中实际使用线程。使用线程的一种方法是创建一个继承java.lang.Thread类的类,实例化它并运行它。
1-2-1 创建一个线程并尝试移动它
在继承Thread的类中,在run方法中写下要在Thread中执行的进程。如果你能正常写入一个方法,你就可以做大部分的事情,比如读取文件、网络通信、数值计算等。
ThreadSample
class ThreadSample extends Thread {
public void run() {
System.out.println(" Running in thread ");
}
}
要运行线程,请创建继承此线程的类的实例并调用start方法。仅此一项,就会创建一个新线程,并且可以在该线程中运行run方法中描述的进程。
ThreadExecutor
class ThreadExecutor {
public static void main(String[] args) {
ThreadSample t = new ThreadSample();
t.start();
}
}
如果您尝试一次运行ThreadExecutor,您将看到以下输出。恭喜。我能够出色地使用线程。
在这里,即使我没有以编程方式调用ThreadSample运行,也会显示字符。换句话说,线程自动调用了run方法。
重要的一点是程序员自己不会调用run。您无法判断线程何时会调用run ,它将在线程准备好时调用。
程序员在使用线程时,有很多事情是无法控制的。与所谓的单线程程序有一些区别。
1-2-2 尝试同时运行多个线程
在之前的程序中,我只是创建了一个线程并运行它。但是,一个线程必须同时运行多个线程,不是吗?
那么接下来,让我们创建三个线程并同时运行它们。现在它变得更像多线程编程。
ThreadSample
class ThreadSample extends Thread {
public void run() {
System.out.println(" 线程 " + getName() + " 上运行 ");
}
}
如果输出消息相同,您将不知道是否有三个不同的线程同时运行,因此请尝试打印出分配给每个线程的唯一名称。
run 中调用的getName方法是Thread类的一个可以获取线程名称的方法。ThreadSample是Thread的子类,因此您可以从run调用getName。
ThreadExecutor
class ThreadExecutor {
public static void main(String[] args) {
ThreadSample t1 = new ThreadSample();
ThreadSample t2 = new ThreadSample();
ThreadSample t3 = new ThreadSample();
t1.start();
t2.start();
t3.start();
}
}
现在让我们再次运行ThreadExecutor 。
线程 Thread-0 上运行 线程 Thread-2 上运行 线程 Thread-1 上运行
并且,像这样输出了3行不同的线程名称。即使程序相同,每个线程的执行结果也会不同。这意味着运行每个进程的线程是不同的 , 三个线程是从当前进程分支出来的,每个线程都是独立运行的 。
1-3 尝试将它与Thread 类和Runnable接口一起使用
使用线程的另一种方法是将要在线程中运行的进程创建为java.lang.Runnable的实现类,并让该Runnable由Thread类的实例运行。实际上,这是使用的。
Runnable 是一个接口,意思是“可以运行的东西”,唯一的抽象方法是返回值void和不带参数的运行。Runnable不仅限于线程,还经常用于表达和实现您希望在Java中运行的处理。
并且使用Runnable,您可以清楚地将线程本身与您希望线程运行的进程分开。这种角色划分也是一种思维方式,可以使程序具有良好的可见性。
1-3-1.1 尝试从一个线程调用
现在让我们来看看实际的程序。RunnableSample实现Runnable。您所做的与继承Thread相同,只是run方法的实现。
RunnableSample
class runnable sample implements runnable {
public void run ( ) {
string thread name = thread.currentthread ( ).getName ( );
System.out.println ("线程" + threadName + "上运行);
}
}
创建 RunnableSample 的实例并将其传递给Thread的构造函数,该构造函数将Runnable作为参数以创建Thread的实例。然后调用Thread的start方法。
ThreadExecutor.java
class ThreadExecutor {
public static void main(String[] args) {
RunnableSample r = new RunnableSample();
Thread t = new Thread(r); // 将 Runnable 传递给线程并实例化
t.start();
}
}
RunnableSample.run 不是以编程方式调用的,而是实际调用的。这与继承Thread时相同,从线程调用run方法。
1-3-2 尝试从一些线程调用
现在让我们从一些线程调用Runnable 。
我创建了三个Thread实例,方法是创建一个 RunnableSample 实例并将其传递给一个将Thread的Runnable作为参数的构造函数。之后,我们开始3 个线程。
ThreadExecutor
class ThreadExecutor {
public static void main(String[] args) {
RunnableSample r = new RunnableSample();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
这里,RunnableSample.run运行时的线程名称都是不同的。但是只有一个RunnableSample实例。这是什么意思。
这意味着由于运行了三个线程,每个线程调用了RunnableSample.run三次,结果为三行。 这意味着可以同时从多个线程调用一个方法。这不仅适用于实例方法,也适用于类方法。此外,实例和类字段也被多个线程同时读写。这些都是非常棘手的编程挑战,但我稍后会告诉你更多关于它们的信息。
2. 2. 如何使用线程方法
Thread 有很多有用的方法。其中,我会简单介绍如何使用程序中出现比较频繁的那些。
2-1 Thread.getId/getName获取线程的标识符/名称
java虚拟机给线程一个编号(标识符)来标识线程。除此之外,Java虚拟机会自动命名(程序员也可以命名)。
获取该线程的标识符和名称的方法是Thread.getId和getName。
ThreadExecutor.java
class ThreadExecutor {
public static void main(String[] args) {
Thread t1 = new Thread();
Thread t2 = new Thread();
//获取线程的标识符
long t1Id = t1.getId();
long t2Id = t2.getId();
//获取线程的名称
String t1Name = t1.getName();
String t2Name = t2.getName();
System.out.println("t1标识符为" + t1Id + "名称为" + t1Name );
System.out.println("t2标识符为" + t2Id + "名称为" + t2Name );
}
}
在程序中,有时您想知道当前进程在哪个线程中运行。然后,您可以使用这些方法找出正在运行的线程。
2-2 获取当前线程Thread.currentThread
从 Thread 继承的类可以很容易地看出你是谁作为一个线程。因为你是线程,所以可以直接调用自己的getId/getName。
ThreadSample.java
class ThreadSample extends Thread {
public void run() {
long id = getId();
String name = getName();
System.out.println("线程标识符:" + id + "name:" + name );
}
}
另一方面,使用Runnable和从线程调用的方法,并不能立即清楚哪个线程正在运行。首先,您不能调用Thread.getId/getName,除非您以某种方式带入当前Thread。
在这种情况下,如果您调用Thread.currentThread ,您可以获得正在运行当前进程的Thread实例。您可以通过从那里调用 getId/getName找出哪个线程。
2-3 Thread.sleep暂停当前线程
有时,您可能想要暂停线程处理。例如,当你想等待一些处理时。在这种情况下,调用Thread.sleep。
您可以调用 Thread.sleep 以在参数指定的秒数内停止当前正在运行的线程。秒的单位是毫秒,重写的方法(Thread.sleep(long millis, int nanos))也可以以纳秒为单位指定。
ThreadSample.java
class ThreadSample extends Thread {
public void run() {
System.out.println("sleep开始");
try {
Thread.sleep(10000L); // 暂停当前线程10秒(10000ms)
} catch (InterruptedException e) {
}
System.out.println("sleep结束");
}
}
只有调用Thread.sleep的线程才会停止。其他线程将继续运行。此外,任何线程的执行都不能直接从另一个线程停止。
这是一个相当难以理解的点。如果sleep没有按预期工作,请使用getId或getName找出您正在睡觉的线程。
如果在停止的线程中发生稍后将描述的中断,则可以中途取消停止状态。在这种情况下,会引发捕获的异常InterruptedException。
最近在做自己的android项目,所有此文章也算是记录吧。
Social Plugin