java线程的作用,线程有啥用
本教程学习线程的基础知识——什么是线程,线程为什么有用,以及如何开始使用线程编写简单的程序。(推荐课程:Java视频教程)
如何解决写爬虫IP受阻的问题?立即使用。
什么是线程?
几乎每个操作系统都支持进程的概念——进程是一个程序,在一定程度上相互隔离,独立运行。
线程是一种允许多个活动共存于一个进程中的工具。大多数现代操作系统都支持线程,线程的概念已经以各种形式存在了很多年。Java是第一种在语言本身中明确包含线程的主流编程语言。它不把线程当作底层操作系统的工具。
有时,线程也被称为轻量级进程。就像进程一样,线程是程序中独立并发的执行路径。每个线程都有自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分离的进程相比,一个进程中线程之间的隔离更少。它们共享内存、文件句柄和每个进程应该拥有的其他状态。
进程可以支持多个线程,这些线程看似同时执行,但彼此并不同步。一个进程中的多个线程共享相同的内存地址空间,这意味着它们可以访问相同的变量和对象,并且它们从同一个堆中分配对象。尽管这使得线程共享信息更容易,但是您必须小心确保它们不会干扰同一进程中的其他线程。
Java线程工具和API看起来很简单。然而,编写有效使用线程的复杂程序并不容易。因为有多个线程共存于同一个内存空间并共享相同的变量,所以您必须小心确保您的线程不会相互干扰。
每个 Java 程序都使用线程
每个Java程序至少有一个线程——主线程。当一个Java程序启动时,JVM会创建一个主线程,并在这个线程中调用程序的main()方法。
JVM还创建了其他线程,这些线程通常是不可见的——例如,与垃圾收集、对象终止和其他JVM内务处理任务相关的线程。其他工具也会创建线程,比如AWT(抽象窗口工具包)或者Swing UI工具箱、servlet容器、应用服务器和RMI(远程方法调用)。
为什么使用线程?
在Java程序中使用线程有很多原因。如果您使用Swing、servlet、RMI或Enterprise JavaBeans(EJB)技术,您可能没有意识到您已经在使用线程了。
使用线程的一些原因是它们可以帮助:
让UI反应更快。
对于多处理器系统
简化建模
执行异步或后台处理。
响应更快的 UI
事件驱动的UI工具箱(如AWT和Swing)有一个处理UI事件的事件线程,如击键或鼠标点击。
AWT和Swing程序将事件侦听器与UI对象连接起来。当一个特定的事件发生时,比如点击一个按钮,这些监听器将被通知。事件侦听器在AWT事件线程中调用。
如果事件侦听器要执行一项长期任务,例如检查一个大文档中的拼写,事件线程将忙于运行拼写检查器,因此在事件侦听器完成之前,不会处理任何其他UI事件。这会让程序看起来停滞不前,让用户不知所措。
为了避免延迟UI响应,事件侦听器应该将一个长任务放在另一个线程中,以便AWT线程可以在任务执行期间继续处理UI事件(包括取消长时间运行的任务的执行请求)。
利用多处理器系统
多处理器(MP)系统比过去更加流行。它们过去只能在大型数据中心和科学计算设施中找到。现在很多低端服务器系统,甚至一些桌面系统,都有多个处理器。
现代操作系统,包括Linux、Solaris和Windows NT/2000,可以利用多个处理器并调度线程在任何可用的处理器上执行。
调度的基本单位通常是线程;如果一个程序只有一个活动线程,它一次只能在一个处理器上运行。如果一个程序有多个活动线程,那么可以同时调度多个线程。在一个设计良好的程序中,使用多线程可以提高程序的吞吐量和性能。
简化建模
在某些情况下,使用线程可以更容易地编写和维护程序。考虑一个模拟应用程序,其中您想要模拟多个实体之间的交互。赋予每个实体自己的线程可以大大简化许多模拟和建模应用程序。
使用独立线程简化程序的另一个例子是当一个应用程序有多个独立的事件驱动组件时。例如,一个应用程序可能有一个组件,它在一个事件发生后倒数秒数并更新屏幕显示。与其让一个主循环定期检查时间,更新显示,不如让一个线程什么都不做,一直睡到一定时间,更新屏幕上的计数器,这样更简单,也不容易出错。这样,主线程就完全不用担心计时器了。
异步或后台处理
服务器应用程序从远程源获取输入,比如套接字。读取套接字时,如果当前没有可用的数据,对SocketInputStream.read()的调用将阻塞,直到有数据可用。
如果单线程程序想要读取套接字,但是套接字另一端的实体不发送任何数据,那么程序只会永远等待,而不会执行其他处理。相反,程序可以轮询套接字以查看是否有可用的数据,但通常不使用这种做法,因为它会影响性能。
但是,如果您创建一个线程来读取套接字,主线程可以在线程等待套接字中的输入时执行其他任务。您甚至可以创建多个线程,以便可以同时读取多个套接字。这样,当有可用数据时,您将很快得到通知(因为等待线程被唤醒),而不必频繁轮询来检查是否有可用数据。使用线程等待套接字的代码也比轮询更简单,更不容易出错。
简单,但有时有风险
虽然Java线程工具非常容易使用,但是在创建多线程程序时,应该尽量避免一些风险。
当多个线程访问同一个数据项(如静态字段、全局可访问对象的实例字段或共享集合)时,有必要确保它们协调对数据的访问,以便它们都能看到一致的数据视图,并且不会干扰彼此的更改。为了实现这个目标,Java语言提供了两个关键词:synchronized和volatile。我们将在本教程的后面研究这些关键字的目的和意义。
当从多个线程访问变量时,必须确保访问是正确同步的。对于简单的变量,将它们声明为volatile可能就足够了,但是在大多数情况下,需要同步。
如果要使用同步来保护对共享变量的访问,必须确保在程序中访问该变量的所有地方都使用同步。
不要做过头
虽然线程可以极大地简化许多类型的应用程序,但是过度使用线程可能会危及程序的性能和可维护性。线程消耗资源。因此,在不降低性能的情况下,可以创建的线程数量是有限的。
尤其是在单处理器系统中,使用多线程并不会让主要消耗CPU资源的程序运行得更快。
示例:使用一个线程用于计时,并使用另一个线程完成工作
下面的例子使用了两个线程,一个用于计时,一个用于实际工作。主线程使用一个非常简单的算法来计算质数。
在它开始之前,它创建并启动一个计时器线程,该线程将休眠十秒钟,然后设置一个标志由主线程检查。十秒钟后,主线程将停止。请注意,共享标志被声明为volatile。
/**
*计算素数-在十秒钟内计算尽可能多的素数
*/
公共类CalculatePrimes扩展线程{
public static final int MAX _ PRIMES=1000000;
公共静态final int TEN _ SECONDS=10000
public volatile boolean完成=false
公共无效运行(){
int[]PRIMES=new int[MAX _ PRIMES];
int count=0;
for(int I=2;countMAX _ PRIMESi ) {
//检查计时器是否已经过期
如果(完成){
打破;
}
布尔质数=真;
for(int j=0;jcountj ) {
if (i % primes[j]==0) {
prime=false
打破;
}
}
if (prime) {
素数[计数]=I;
system . out . println( Found prime: I );
}
}
}
公共静态void main(String[] args) {
CalculatePrimes计算器=new calculate primes();
calculator . start();
尝试{
Thread.sleep(十_秒);
}
catch(中断异常e) {
//落空
}
calculator.finished=true
}
}小结
Java语言包含强大的内置线程工具。您可以使用螺纹工具来:
提高GUI应用程序的响应速度
对于多处理器系统
当程序有多个独立实体时,简化程序逻辑。
在不阻塞整个程序的情况下执行阻塞I/O。
使用多线程时,必须小心谨慎,并遵循线程间共享数据的规则。我们将在共享数据访问中讨论这些规则。所有这些规则归结为一个基本原则:不要忘记同步。这就是java线程的使用细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。