前言。彼佳叔叔
假设我们想装一瓶水。提供一瓶和Uncle Petya 的水。今天佩蒂亚叔叔安装了一个新水龙头,他一直称赞它很漂亮。在此之前,他只使用一个旧的、堵塞的水龙头,因此装瓶处的生产线非常长。摸索了一下,从溢出的方向传来了灌水的声音,两分钟后,瓶子还处于灌水阶段,身后已经聚集了平常的队列,脑海中浮现出大叔多么贴心的画面。 Petya 只选择最好的 H2O 分子放入我们的瓶子中。受过生活训练的佩蒂亚叔叔会安抚那些特别好斗的人,并承诺尽快完成任务。喝完一瓶后,他又拿起下一瓶,并打开通常的压力,这并没有显示出新水龙头的所有功能。人家不高兴了...
理论
多线程是平台在单个进程中创建多个线程的能力。创建和执行线程比创建进程简单得多,因此如果需要在一个程序中实现多个并行操作,则需要使用额外的线程。在JVM中,任何程序都在主线程中运行,其余程序都从主线程启动。在同一进程内,线程之间可以交换数据。当启动一个新线程时,可以使用以下方法将其声明为用户线程
setDaemon(true);
如果没有其他正在运行的线程,此类线程将自动终止。线程具有工作优先级(优先级的选择并不能保证最高优先级线程比较低优先级线程更快完成)。
- MIN_PRIORITY
- NORM_PRIORITY(默认)
- 最大优先级
使用流时的基本方法:
run()
– 执行线程
start()
– 启动一个线程
getName()
– 返回线程名称
setName()
– 指定流的名称
wait()
notify()
– 继承的方法,线程等待从另一个线程调用该方法
notify()
– 继承的方法,恢复先前停止的线程
notifyAll()
– 继承的方法,恢复先前停止的线程
sleep()
– 将流暂停指定时间
join()
– 等待线程完成
interrupt()
– 中断线程执行
更多方法可以在这里找到
。 如果你的程序有以下情况,那么是时候考虑新线程了:
线程类
Java 中的线程表示为一个类
Thread
及其后代。下面的示例是流类的简单实现。
import static java.lang.System.out;
public class ExampleThread extends Thread{
public static void main(String[] args) {
out.println("Основной поток");
new ExampleThread().start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
结果我们得到
Основной поток
Новый поток
在这里,我们创建了我们的类并使其成为该类的后代
Thread
,然后编写 main() 方法来启动主线程并重写
run()
类方法
Thread
。现在,创建了类的实例并执行了其继承的方法后,
start()
我们将启动一个新线程,在该线程中将执行方法体中描述的所有内容
run()
。听起来很复杂,但是看看示例代码一切都应该很清楚。
可运行接口
Oracle还建议实现该接口来启动新线程
Runnable
,这比上一个示例中唯一可用的继承为我们提供了更多的设计灵活性(如果您查看该类的源代码,
Thread
您可以看到它也实现了该接口
Runnable
)。让我们使用推荐的方法来创建新线程。
import static java.lang.System.out;
public class ExampleRunnable implements Runnable {
public static void main(String[] args) {
out.println("Основной поток");
new Thread(new ExampleRunnable()).start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
结果我们得到
Основной поток
Новый поток
这些例子非常相似,因为 在编写代码时,我们必须实现
run()
接口中描述的抽象方法
Runnable
。启动新线程有点不同。
Thread
我们通过将接口实现实例的引用作为参数传递来创建该类的实例
Runnable
。正是这种方法允许您创建新线程而无需直接继承该类
Thread
。
长时间操作
下面的例子将清楚地展示使用多线程的好处。假设我们有一个简单的任务,需要多次冗长的计算,在本文之前,我们会用一种方法来解决它,
main()
也许将其分解为单独的方法以便于感知,甚至可能是类,但本质是相同的。所有操作都将按顺序依次执行。让我们模拟重量级计算并测量它们的执行时间。
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 1");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 2");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
结果我们得到
complete 1
complete 2
complete 3
программа выполнялась 9885 миллисекунд
执行时间还有很多不足之处,一直以来我们都在看着一个空白的输出屏幕,情况与有关 Petya 叔叔的故事非常相似,只是现在在他的角色中,我们开发人员还没有利用现代设备的所有功能。我们会改进。
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
new MyThread(1).start();
new MyThread(2).start();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
class MyThread extends Thread{
int n;
MyThread(int n){
this.n = n;
}
@Override
public void run() {
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete " + n);
}
}
结果我们得到
complete 1
complete 2
complete 3
программа выполнялась 3466 миллисекунд
运行时间已显着减少(在不支持多线程的处理器上可能无法实现此效果,甚至可能增加执行时间)。值得注意的是,线程可能会无序结束,如果开发人员需要操作的可预测性,他必须针对特定情况独立实现。
线程组
Java 中的线程可以组合成组;该类就是用于此目的的
ThreadGroup
。组可以包括单个线程和整个组。如果您需要中断关联的流(例如,在连接丢失时与网络相关的流),这会很方便。
您可以在此处阅读有关群组的更多信息, 我希望您现在已经更清楚该主题,并且您的用户会感到高兴。
GO TO FULL VERSION