发布于:2021-01-21 11:39:03
0
146
0
本文讲述了线程的故事,并解释了它的主要优点、缺点和用途。您将从熟练的Java程序员那里学习如何使用扩展线程类和实现可运行接口来创建线程。
螺纹的优点
缩短开发时间。
降低维护成本。
提高复杂应用程序的性能。
有助于提高用户界面的响应能力。
用于服务器应用程序,以提高高吞吐量和资源利用率。
并行化任务。
如果线程无法执行任务使用CPU的所有计算资源(因为指令依赖于彼此的结果),运行另一个线程可以避免这些线程处于空闲状态。
利用多处理器系统。
螺纹的缺点
在共享硬件资源(如缓存或转换查找缓冲区(TLB))时,多个线程可能会相互干扰。
单个线程的执行时间可能会降低,即使只有一个线程在执行。这是由于适应线程切换硬件所需的较慢的频率和/或额外的流水线级。
软件对多线程的硬件支持更为明显,因此需要对应用程序和操作系统进行比多处理更多的更改。
螺纹的用途
Java应用程序是自然线程化的。运行时环境使用一个线程中的main()方法开始执行程序。垃圾回收发生在另一个线程中。屏幕更新发生在第三个线程中。可能还有其他线程也在运行,主要与虚拟机的行为有关。所有这些都发生在程序员身上。有时您只关心主线程中发生了什么,其中包括程序的main()方法。如果是这种情况,您可能根本不需要担心线程问题。
多线程的主要目的是提供同时执行程序的两个或多个部分,以尽可能多地利用CPU时间。多线程程序包含两个或多个可以并发运行的部分。这样一个程序的每一部分都称为线程。每个线程都有一个单独的执行路径。这样一个程序就可以同时执行两个或多个任务。
线程是轻量级进程;它们共享相同的地址空间。在多线程环境中,程序最大限度地利用CPU,以便将空闲时间保持在最小值。
执行异步或后台处理。
线程
Thread类是程序中执行的线程。Java虚拟机允许应用程序同时运行多个执行线程。以下是最重要的几点:
每个线程都有一个优先级。优先级较高的线程在优先级较低的线程之前执行。
每个线程也可以标记为守护进程,也可以不标记为守护进程。
创建新的执行线程有两种方法。一种是将类声明为线程的子类。
另一种创建线程的方法是声明实现可运行接口的类。
线程类提供了用于在线程类上创建和执行操作的构造函数和方法。线程类扩展了Object类并实现了Runnable接口。
线程类的构造函数
线程()
线程(String str)
线程(Runnable r)
线程(Runnable r,String str)
Thread类定义了许多用于管理线程的方法。其中一些是:
方法 | 描述 |
setName() | 给线程起个名字 |
getName() | 返回线程的名称 |
getPriority() | 返回线程的优先级 |
isAlive() | 检查线程是否仍在运行 |
join() | 等待线程结束 |
run() | 线程的入口点 |
sleep() | 将线程挂起指定的时间 |
start() | 通过调用run()方法启动线程 |
有两种创建线程的方法:
扩展线程类
实现Runnable接口
1.扩展线程类
通过扩展Thread类的新类创建线程,并创建该类的实例。扩展类必须重写run()方法,该方法是新线程的入口点。
public class MyThread extends Thread{ public void run() { System.out.println("Thread started running.."); } public static void main( String args[] ) { MyThread mt = new MyThread(); mt.start(); } }
在这种情况下,我们必须重写run(),然后使用start()方法启动并运行线程。同样,当您创建MyThread类对象时,由于它是超类,因此也将调用Thread类构造函数,因此MyThread类对象充当Thread类对象。
我们直接调用run()方法,而无需使用start()方法。
public static void main( String args[] ) { MyThread mt = new MyThread(); mt.start(); mt.start(); //Exception thrown }
不会为该线程分配新的调用堆栈,它将开始在当前调用堆栈(即主线程的调用堆栈)中运行。因此,多线程将不存在。
我们启动一个线程两次。
线程不能启动两次。如果您尝试这样做,将抛出IllegalThreadStateException。
public static void main( String args[] ) { MyThread mt = new MyThread(); mt.start(); mt.start(); //Exception thrown }
当线程处于运行状态时,您尝试重新启动它,或者任何方法尝试使用start()方法再次调用该线程,都会引发异常。
2.实现可运行接口
创建线程的最简单方法是创建一个实现可运行接口的类。在实现runnable接口之后,该类需要实现run()方法,即
公共无效run()
run()方法将并发线程引入程序。当run()返回时,该线程将结束。
您必须在run()方法中为线程指定代码。
run()方法可以调用其他方法,可以使用其他类并声明变量,就像其他任何普通方法一样。
MyThread类实现Runnable
public void run() { System.out.println("Thread started running.."); } public static void main(String args[]) { MyThread mt = new MyThread(); Thread t = new Thread(mt); t.start(); } }
要调用run()方法,请使用start()方法。在调用start()时,会向线程提供一个新堆栈,并调用run()方法将新线程引入程序
结束线程
线程结束的原因如下:
当run()方法完成其执行时线程结束。
当线程抛出程序中未捕获的异常或错误时。
Java程序完成或结束。
另一个线程调用stop()方法。
线程同步
当多个线程试图访问共享资源时,我们需要确保资源一次只能由一个线程使用。实现这一点的过程称为同步。
Java中的synchronization关键字创建了一个称为critical section的代码块。
通过使用这个,一次只有一个线程可以访问该方法,第二个调用将被阻止,直到第一个调用返回或在内部调用wait()同步方法。
语法
synchronized (object) { // Statement to be synchronized }
使用同步
如果不使用同步,让两个或多个线程同时访问共享资源,将导致扭曲的结果。
下面是一个示例:假设我们有两个不同的线程T1和T2,T1开始执行并将某些值保存到文件示例.txt当T1返回时,它将用于计算一些结果。同时,T2开始,在T1返回之前,T2更改T1保存在文件中的值示例.txt(sample.txt是共享资源)。现在很明显,T1会给出错误的结果。
同步被引入以防止此类问题的发生。如果我们在上述情况下使用同步,那么一旦T1开始使用示例.txt文件,此文件将被锁定(锁定模式),在T1返回之前,其他线程将无法访问或修改它。
僵局
死锁描述了两个或多个线程被永远阻塞,互相等待的情况。当多个线程需要相同的锁但以不同的顺序获得它们时,就会发生死锁。Java多线程程序可能会遇到死锁情况,因为关键字synchronized会导致执行线程在等待与指定对象关联的锁或监视器时阻塞。
为了避免死锁,应确保在获取多个锁时,在所有线程中总是以相同的顺序获取锁。
Java程序员在这里讨论的关于线程的每一点都是为了让您了解线程及其用法以及缺点和好处。
作者介绍