浅谈Java多线程的优点及代码示例
尽管面临很多挑战,多线程有一些优点使得它一直被使用。这些优点是:
资源利用率更好
程序设计在某些情况下更简单
程序响应更快
资源利用率更好
想象一下,一个应用程序需要从本地文件系统中读取和处理文件的情景。比方说,从磁盘读取一个文件需要5秒,处理一个文件需要2秒。处理两个文件则需要:
5秒读取文件A 2秒处理文件A 5秒读取文件B 2秒处理文件B --------------------- 总共需要14秒
从磁盘中读取文件的时候,大部分的CPU时间用于等待磁盘去读取数据。在这段时间里,CPU非常的空闲。它可以做一些别的事情。通过改变操作的顺序,就能够更好的使用CPU资源。看下面的顺序:
5秒读取文件A 5秒读取文件B + 2秒处理文件A 2秒处理文件B --------------------- 总共需要12秒
CPU等待第一个文件被读取完。然后开始读取第二个文件。当第二文件在被读取的时候,CPU会去处理第一个文件。记住,在等待磁盘读取文件的时候,CPU大部分时间是空闲的。
总的说来,CPU能够在等待IO的时候做一些其他的事情。这个不一定就是磁盘IO。它也可以是网络的IO,或者用户输入。通常情况下,网络和磁盘的IO比CPU和内存的IO慢的多。
程序设计更简单
在单线程应用程序中,如果你想编写程序手动处理上面所提到的读取和处理的顺序,你必须记录每个文件读取和处理的状态。相反,你可以启动两个线程,每个线程处理一个文件的读取和操作。线程会在等待磁盘读取文件的过程中被阻塞。在等待的时候,其他的线程能够使用CPU去处理已经读取完的文件。其结果就是,磁盘总是在繁忙地读取不同的文件到内存中。这会带来磁盘和CPU利用率的提升。而且每个线程只需要记录一个文件,因此这种方式也很容易编程实现。
程序响应更快
将一个单线程应用程序变成多线程应用程序的另一个常见的目的是实现一个响应更快的应用程序。设想一个服务器应用,它在某一个端口监听进来的请求。当一个请求到来时,它去处理这个请求,然后再返回去监听。
服务器的流程如下所述:
while(server is active){ listen for request process request }
如果一个请求需要占用大量的时间来处理,在这段时间内新的客户端就无法发送请求给服务端。只有服务器在监听的时候,请求才能被接收。另一种设计是,监听线程把请求传递给工作者线程(worker thread),然后立刻返回去监听。而工作者线程则能够处理这个请求并发送一个回复给客户端。这种设计如下所述:
while(server is active){ listen for request hand request to worker thread }
这种方式,服务端线程迅速地返回去监听。因此,更多的客户端能够发送请求给服务端。这个服务也变得响应更快。
桌面应用也是同样如此。如果你点击一个按钮开始运行一个耗时的任务,这个线程既要执行任务又要更新窗口和按钮,那么在任务执行的过程中,这个应用程序看起来好像没有反应一样。相反,任务可以传递给工作者线程(word thread)。当工作者线程在繁忙地处理任务的时候,窗口线程可以自由地响应其他用户的请求。当工作者线程完成任务的时候,它发送信号给窗口线程。窗口线程便可以更新应用程序窗口,并显示任务的结果。对用户而言,这种具有工作者线程设计的程序显得响应速度更快。
下面是借鉴别人的一个Java多线程的例子,感谢。
问题:三个售票窗口同时出售20张票;
程序分析:
1.票数要使用同一个静态值
2.为保证不会出现卖出同一个票数,要java多线程同步锁。
设计思路:1.创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!
创建主方法调用类
(一)创建一个站台类,继承Thread
(原文有一点点小错误,小编已经更正,大家请放心测试使用)
package com.xykj.threadStation; public class Station extends Thread { // 通过构造方法给线程名字赋值 public Station(String name) { super(name); // 给线程名字赋值 } // 为了保持票数的一致,票数要静态 static int tick = 20; // 创建一个静态钥匙 static Object ob = "aa"; //值是任意的 // 重写run方法,实现买票操作 @Override public void run() { while (tick > 0) { synchronized (ob) { // 这个很重要,必须使用一个锁, // 进去的人会把钥匙拿在手上,出来后才把钥匙拿让出来 if (tick > 0) { System.out.println(getName() + "卖出了第" + tick + "张票"); tick--; } else { System.out.println("票卖完了"); } } try { sleep(1000); //休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
(二)创建主方法调用类
package com.xykj.threadStation; public class MainClass { /** * java多线程同步锁的使用 * 示例:三个售票窗口同时出售10张票 * */ public static void main(String[] args) { //实例化站台对象,并为每一个站台取名字 Station station1=new Station("窗口1"); Station station2=new Station("窗口2"); Station station3=new Station("窗口3"); // 让每一个站台对象各自开始工作 station1.start(); station2.start(); station3.start(); } }
运行结果:
窗口1卖出了第20张票 窗口2卖出了第19张票 窗口3卖出了第18张票 窗口1卖出了第17张票 窗口2卖出了第16张票 窗口3卖出了第15张票 窗口3卖出了第14张票 窗口2卖出了第13张票 窗口1卖出了第12张票 窗口3卖出了第11张票 窗口2卖出了第10张票 窗口1卖出了第9张票 窗口1卖出了第8张票 窗口2卖出了第7张票 窗口3卖出了第6张票 窗口1卖出了第5张票 窗口3卖出了第4张票 窗口2卖出了第3张票 窗口3卖出了第2张票 窗口2卖出了第1张票 票卖完了
总结
以上就是本文关于浅谈Java多线程的优点及代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅:Java多线程之线程通信生产者消费者模式及等待唤醒机制代码详解、Java编程之多线程死锁与线程间通信简单实现代码、Java多线程编程小实例模拟停车场系统等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!
相关文章
Spring中的@PostConstruct注解使用方法解析
这篇文章主要介绍了Spring中的@PostConstruct注解使用方法解析,@PostConstruct注解是用来处理在@Autowired注入属性后init()方法之前,对一些零散的属性进行赋值的注解,需要的朋友可以参考下2023-11-11Netty中的DelimiterBasedFrameDecoder使用方法详解
这篇文章主要介绍了Netty中的DelimiterBasedFrameDecoder使用方法详解,DelimiterBasedFrameDecoder与LineBasedFrameDecoder类似,只不过更加通用,允许我们指定任意特殊字符作为分隔符,我们还可以同时指定多个分隔符,需要的朋友可以参考下2023-12-12SpringBoot使用@Validated处理校验的方法步骤
@Validated 注解的主要目的是启用和利用 Spring 的验证框架,它可以用于类上也可以用于方法参数上,本文给大家介绍了SpringBoot使用@Validated优雅的处理校验的方法步骤,通过代码示例介绍的非常详细,需要的朋友可以参考下2024-08-08java.lang.AbstractMethodError: org.apache.xerces.dom.Documen
这篇文章主要介绍了java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion问题解决方法,导致本文问题的原因是缺少一个xerces.jar jar包,需要的朋友可以参考下2015-03-03详解JavaEE使用过滤器实现登录(用户自动登录 安全登录 取消自动登录黑用户禁止登录)
主要介绍用户的自动登录和取消自动登录,以及实现一天自动登录或者n天实现自动登录,当用户ip被加入到黑名单之后,直接利用过滤器返回一个警告页面。接下来通过本文给大家介绍JavaEE使用过滤器实现登录的相关知识,感兴趣的朋友一起学习吧2016-05-05
最新评论